LEFT JOIN与RIGHT条件

时间:2018-01-18 23:06:57

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

比方说,我有两张桌子:

  • Student_Class Student_IDClass_ID
  • Class_IDAttribute_ID和其他字段

[底部的示例数据]

要求:

  1. 对于给定的学生,列出具有给定属性的类的相应值。
  2. 如果学生没有使用此特定属性的课程 - 请列出NULL。
  3. 如果保证包含NULL的行 - 每个学生只生成一个这样的行
  4. 没有条件#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,并UNION将其与INNER JOIN结果一起动态创建。

    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_Class:

    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
    

3 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

我认为你的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 (...);

答案 2 :(得分:0)

您将必须运行一个连接两个子查询结果的查询:

  • 具有匹配班级的学生列表
  • 所有学生的清单

获取所有具有匹配课程的学生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

我们还需要一份所有学生的清单。这个数据库几乎肯定有一个学生表,但是为了这个练习,我们假装它没有。我们将通过此查询获取学生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 (...)