首页 > 解决方案 > SQLServer 通过两个条件将结果限制为 1

问题描述

我的公司提供不同的课程。一门课程通常持续五天,通常由一位老师教授。但是,也有一些例外情况,即一门课程在两位老师之间分开。目前我有一个 SQL 语句,它在课程的第一天选择(或“限制”)结果给老师:

SELECT c.course_id,
       c.description,
       t.firstname AS teacher_firstname,
       t.lastname AS teacher_lastname,
       cd.day AS first_day,
       cd2.day AS last_day
FROM (SELECT *,
             ROW_NUMBER() OVER (PARTITION BY course_id ORDER BY day) AS rn
      FROM Course_days) cd
     JOIN (SELECT *,
                  ROW_NUMBER() OVER (PARTITION BY course_id ORDER BY day DESC) AS rn2
           FROM Course_days) cd2 ON cd.course_id = cd2.course_id
     JOIN Courses c ON cd.course_id = c.course_id
     JOIN Teachers t ON t.teacher_id = cd.teacher_id
WHERE rn = 1
  AND rn2 = 1;

它返回如下内容:

course_id 描述 老师名字 老师姓氏 第一天 最后一天
101 HTML/CSS/JS 基础知识 2021 年 2 月 约翰 能源部 2021-02-01 2021-02-05
102 C# 2021 年 2 月 能源部 2021-02-01 2021-02-05
... ... ... ... ... ...

正如您在下面的 DB 结构中看到的,课程 101 有两名教师(粗体条目)。

有没有可能得到这样的结果?

course_id 描述 老师名字 老师姓氏 second_teacher_firstname second_teacher_lastname 第一天 最后一天
101 HTML/CSS/JS 基础知识 2021 年 2 月 约翰 能源部 先生。 X 2021-02-01 2021-02-05
102 C# 2021 年 2 月 能源部 无效的 无效的 2021-02-01 2021-02-05
... ... ... ... ... ...

我已经花了几个小时寻找一种可能的方法来做到这一点,但并没有真正找到任何东西。

数据库结构:

培训班:

course_id 描述
... ...
101 HTML/CSS/JS 基础知识 2021 年 2 月
102 C# 基础知识 2021 年 2 月
103 Java 基础知识 2021 年 2 月
104 HTML/CSS/JS 基础知识 2021 年 3 月
105 C# 基础知识 2021 年 3 月
106 Java 基础知识 2021 年 3 月
... ...

教师:

教师编号
1 约翰 能源部
2 能源部
3 前任 充足
4 先生。 X
... ... ...

Course_Days:

course_day_id course_id (外键) Teacher_id (外键) course_day_number
... ... ... ... ...
548 101 1 2021-02-01 1
549 101 1 2021-02-02 2
550 101 1 2021-02-03 3
551 101 1 2021-02-04 4
552 101 4 2021-02-05 5
553 102 2 2021-02-01 1
554 102 2 2021-02-02 2
555 102 2 2021-02-03 3
556 102 2 2021-02-04 4
557 102 2 2021-02-05 5
... ... ... ... ...

标签: sqlsql-server

解决方案


那些老师很棘手,因为您(大概)想要保留原始顺序。

这里采用的方法可以推广到更多的教师,并保持名称有序。对于每位教师,它决定了教师教授课程的第一天。然后它使用条件聚合来定义“teacher1”和“teacher2”:

SELECT c.course_id, c.description,
       MAX(CASE WHEN teacher_seqnum = 1 THEN t.firstname END) AS teacher1_firstname,
       MAX(CASE WHEN teacher_seqnum = 1 THEN t.lastname END) AS teacher1_lastname,
       MAX(CASE WHEN teacher_seqnum = 2 THEN t.firstname END) AS teacher2_firstname,
       MAX(CASE WHEN teacher_seqnum = 2 THEN t.lastname END) AS teacher2_lastname,
       MIN(day) as first_day,
       MAX(day) as last_day
FROM (SELECT cd.*,
             dense_rank() OVER (PARTITION BY course_id, teacher_id ORDER BY min_teacher_day) as teacher_seqnum
       FROM (SELECT c.description, cd.*,
                    t.firstname, t.lastname,
                    ROW_NUMBER() OVER (PARTITION BY cd.course_id ORDER BY cd.day) AS seqnum,
                    MIN(cd.day) OVER (PARTITION BY cd.course_id, cd.teacher_id) as min_teacher_day
             FROM Course_days cd JOIN
                  Teachers t
                  ON t.teacher_id = cd.teacher_id JOIN
                  Courses c
                  ON c.course_id = cd.course_id
            ) cd
     ) cd
GROUP BY course_id, description

推荐阅读