【问题标题】:LEFT JOIN with RIGHT conditionLEFT JOIN 与 RIGHT 条件
【发布时间】:2018-01-18 23:06:56
【问题描述】:

假设,我有两张桌子:

  • 学生班级Student_IDClass_ID
  • Class_IDAttribute_ID等字段

[底部的样本数据]

要求:

  1. 对于给定的学生,列出具有给定属性的班级的对应值。
  2. 如果学生不参加具有此特定属性的课程 - 请改为列出 NULL。
  3. 如果保证有 NULL 的行 - 每个学生只生成一个这样的行

没有条件 #2 和 #3 是简单的INNER JOIN

SELECT sc.student_id, c.*
FROM Student_class sc
JOIN Class c ON sc.class_id=c.class_id
WHERE c.attribute_id = ... AND sc.student_id IN (...)

没有#3,它是OUTER JOIN

SELECT sc.student_id, c.*
FROM Student_class sc
LEFT JOIN Class c ON sc.class_id=c.class_id AND c.attribute_id = ...
WHERE sc.student_id IN (...)

满足所有三个要求 - 我正在苦苦挣扎。

SELECT DISTINCT sc.student_id, c.*
FROM Student_class sc
LEFT JOIN Class c ON sc.class_id=c.class_id AND c.attribute_id = ...
WHERE sc.student_id IN (...)

无论是否找到具有该属性的类,以上都会产生 NULL 行


我正在考虑为列表中的每个学生动态创建NULL row,并使用INNER JOIN 结果创建UNION

SELECT student_id, NULL, NULL, ... , ... 

UNION 

SELECT sc.student_id, c.*
FROM Student_class sc
JOIN Class c ON sc.class_id=c.class_id
WHERE c.attribute_id = ... AND sc.student_id IN (...)

上述要求在第一个查询中使用正确数量的 NULL,因此无法承受对表的更改。


我在这里遗漏了一些明显的东西吗?


样本数据:

学生班级:

Student_ID   Class_ID

0001         0050
0001         0150
0002         0050
0002         0100
0002         0155
0002         1200
0002         1155
0003         1155
0004         0050
0004         0155

类:

Class_ID   Attribute_ID  Value1 Value2 Value3

0050       A1            1      2      3
0100       A2            4      5      6
0150       A3            7      8      9
0155       A1            9      8      7
1155       A4            6      5      4
1200       A4            3      2      1

Student ID 的期望结果:0001、0002、0003 和 Attribute_ID:A1。

Student_Id Class_ID   Attribute_ID  Value1 Value2 Value3

0001       050        A1            1      2      3
0002       050        A1            1      2      3
0002       155        A1            9      8      7
0003       NULL       NULL          NULL   NULL   NULL

【问题讨论】:

  • 能否提供样本数据?
  • @FelixPamittan - 已添加。希望没有错别字。

标签: sql join many-to-many ansi-sql


【解决方案1】:

这可以使用 CTE 和分区函数 (ROW_NUMBER()) 轻松实现

DECLARE @Student_Class TABLE (Student_ID varchar(10), Class_ID  varchar(10)); 

INSERT INTO @Student_Class VALUES('0001', '0050');
INSERT INTO @Student_Class VALUES('0001', '0150');
INSERT INTO @Student_Class VALUES('0002', '0050');
INSERT INTO @Student_Class VALUES('0002', '0100');
INSERT INTO @Student_Class VALUES('0002', '0155');
INSERT INTO @Student_Class VALUES('0002', '1200');
INSERT INTO @Student_Class VALUES('0002', '1155');
INSERT INTO @Student_Class VALUES('0003', '1155');
INSERT INTO @Student_Class VALUES('0004', '0050');
INSERT INTO @Student_Class VALUES('0004', '0155');

DECLARE @Class TABLE (Class_ID varchar(10), Attribute_ID varchar(10), Value1 int); 

INSERT INTO @Class VALUES('0050',  'A1', 1);
INSERT INTO @Class VALUES('0100',  'A2', 4);
INSERT INTO @Class VALUES('0150',  'A3', 7);
INSERT INTO @Class VALUES('0155',  'A1', 9);
INSERT INTO @Class VALUES('1155',  'A4', 6);
INSERT INTO @Class VALUES('1200',  'A4', 3);

WITH TempData_CTE (Student_ID, Class_ID, Attribute_ID, Value1, rowNum)  
AS
(
    SELECT  sc.Student_ID, 
            c.Class_ID, 
            c.Attribute_ID, 
            c.Value1,
            ROW_NUMBER() OVER (PARTITION BY sc.Student_ID ORDER BY sc.Student_ID DESC) AS rowNum
    FROM    @Student_Class sc 
                LEFT JOIN @Class c ON sc.class_id = c.class_id AND c.Attribute_ID = 'A1'
    WHERE   sc.Student_ID IN (0001, 0002, 0003)
)
SELECT  Student_ID, Class_ID, Attribute_ID, Value1 
FROM    TempData_CTE 
WHERE   Class_ID IS NOT NULL OR rowNum = 1

【讨论】:

  • 我喜欢你的方法,谢谢。
【解决方案2】:

我认为您的 left join 应该可以正常工作:

SELECT sc.student_id, c.*
FROM Student_class sc LEFT JOIN
     Class c
     ON sc.class_id = c.class_id AND c.attribute_id = ...
WHERE sc.student_id IN (...);

如果没有匹配,那么对于每个不匹配的学生和每个班级,您将只获得一行。如果一个学生可能有多个不匹配的课程,您可以这样做:

SELECT DISTINCT sc.student_id, c.*
FROM Student_class sc LEFT JOIN
     Class c
     ON sc.class_id = c.class_id AND c.attribute_id = ...
WHERE sc.student_id IN (...);

但是,我个人会为每个不匹配的类一行一行,并且我会在查询中包含该类:

SELECT sc.student_id, sc.class_id, c.*
FROM Student_class sc LEFT JOIN
     Class c
     ON sc.class_id = c.class_id AND c.attribute_id = ...
WHERE sc.student_id IN (...);

【讨论】:

    【解决方案3】:

    您将不得不运行一个查询来连接两个子查询的结果:

    • 匹配班级的学生名单
    • 所有学生的不同列表

    获取所有具有匹配班级的学生 ID 的列表,(但不一定是所有学生 ID)。

    SELECT
       Student_Class.Student_Id,
       Student_Class.Class_Id 
    from
       Student_Class 
       INNER JOIN
          Class 
          ON Student_Class.Class_Id = Class.Class_Id 
    WHERE
       Class.Attribute_ID = @Attribute_ID
    

    我们还需要一份所有学生的不同名单。这个数据库几乎肯定有一个 Student 表,但是为了这个练习,我们假设它没有。我们将通过此查询获取学生 ID 列表:

    SELECT DISTINCT Student_Class.Student_ID from Student_Class
    

    现在我们将结合这两个查询

    SELECT
       Students.ID,
       Matching_Classes.Class_ID 
    FROM
       (
          SELECT DISTINCT
             Student_Class.Student_ID 
          from
             Student_Class 
       )
       Students 
       LEFT OUTER JOIN
          (
             SELECT
                Student_Class.Student_Id,
                Student_Class.Class_Id 
             FROM
                Student_Class 
                INNER JOIN
                   Class 
                   on Student_Class.Class_Id = Class.Class_Id 
             WHERE
                Class.Attribute_ID = @Attribute_ID
          )
          Matching_Classes 
          ON Students.Student_Id = Matching_Classes.Student_Id
     WHERE Students.ID IN (...)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-12
      • 2014-07-30
      • 1970-01-01
      • 1970-01-01
      • 2013-05-02
      • 1970-01-01
      • 2018-09-04
      相关资源
      最近更新 更多