【问题标题】:Oracle - Finding missing /non-joined recordsOracle - 查找丢失/未加入的记录
【发布时间】:2021-01-17 12:58:12
【问题描述】:

我在 Oracle 12 中遇到了一个问题,这个问题最容易用传统的数据库设计方案来解释,即学生、班级和参加称为注册的课程的学生。我很理解这个模型。我有一个场景,我需要得到一个完整的列表,所有学生对所有课程,以及他们是否参加该课程......

让我们在这里使用这个表格设计...

CREATE TABLE CLASSES 
(CLASSID  VARCHAR2(10) PRIMARY KEY,
CLASSNAME  VARCHAR2(25),
INSTRUCTOR VARCHAR2(25) );


CREATE TABLE STUDENTS
(STUDENTID  VARCHAR2(10) PRIMARY KEY,
STUDENTNAMENAME  VARCHAR2(25)
STUDY_MAJOR VARCHAR2(25) );

CREATE TABLE REGISTRATION
(
 CLASSID VARCHAR2(10 BYTE), 
 STUDENTID VARCHAR2(10 BYTE), 
 GRADE NUMBER(4,0), 
 CONSTRAINT "PK1" PRIMARY KEY ("CLASSID", "STUDENTID"),
 CONSTRAINT "FK1" FOREIGN KEY ("CLASSID") REFERENCES "CLASSES" ("CLASSID") ENABLE, 
 CONSTRAINT "FK2" FOREIGN KEY ("STUDENTID") REFERENCES "EGR_MM"."STUDENTS" ("STUDENTID") ENABLE
 ) ;

因此假设以下... 300 名学生和 15 个不同的班级... 注册表将显示有多少学生参加了多少个班级... 我需要的是该信息加上所有未采取的组合。 ..即我需要一个显示所有可能组合的报告(SQL语句)......即300 x 15,然后该行是否存在于注册表中......例如,输出应该如下所示......

STUDENTID   Class1_GRADE  Class2_Grade      Class3_Grade`       Class4_Grade
101         A               B                   Not Taking          A
102         C               Not Taking          Not Taking          Not Taking
****** THIS STUDENT NOT TAKING ANY CLASSES So NOT in the Registrations Table
103         Not Taking      Not Taking          Not Taking          Not Taking  

这也可以,我可能可以做一个 PIVOT 来获得上面的列表。

STUDENTID   CLASSID  GRADE
101         Class1    A
101         Class2    B
101         Class3    Not Taking
101         Class4    A
...
102         Class1    C
102         Class2    Not Taking
102         Class3    Not Taking
102         Class4    Not Taking
...
103         Class1    Not Taking  // THIS STUDENT NOT TAKING ANY CLASSES
103         Class2    Not Taking
103         Class3    Not Taking
103         Class4    Not Taking

我该如何填写缺失的数据,即未上过的学生和班级的组合...?

【问题讨论】:

    标签: sql oracle anti-join


    【解决方案1】:

    CROSS JOIN 学生和班级然后LEFT OUTER JOIN 注册然后使用COALESCE 获取Not taken 值:

    SELECT s.studentid,
           c.classid,
           COALESCE( TO_CHAR( r.grade ), 'Not taken' ) AS grade
    FROM   students s
           CROSS JOIN classes c
           LEFT OUTER JOIN registration r
           ON ( s.studentid = r.studentid AND c.classid = r.classid )
    

    如果你有数据的话:

    INSERT INTO Classes
    SELECT LEVEL,
           'Class' || LEVEL,
           'Instructor' || LEVEL
    FROM   DUAL
    CONNECT BY LEVEL <= 3;
    
    INSERT INTO Students
    SELECT TO_CHAR( LEVEL, 'FM000' ),
           'Student' || LEVEL,
           'Major'
    FROM   DUAL
    CONNECT BY LEVEL <= 5;
    
    INSERT INTO Registration
    SELECT 1, '001', 4 FROM DUAL UNION ALL
    SELECT 1, '002', 2 FROM DUAL UNION ALL
    SELECT 1, '003', 5 FROM DUAL UNION ALL
    SELECT 2, '001', 3 FROM DUAL UNION ALL
    SELECT 3, '001', 1 FROM DUAL;
    

    然后输出:

    学生证 |类 |年级 :-------- | :-------- | :-------- 001 | 1 | 4 002 | 1 | 2 003 | 1 | 5 001 | 2 | 3 001 | 3 | 1 005 | 1 |不采取 004 | 2 |不采取 003 | 3 |不采取 005 | 3 |不采取 005 | 2 |不采取 002 | 2 |不采取 003 | 2 |不采取 004 | 1 |不采取 002 | 3 |不采取 004 | 3 |不采取

    如果你想旋转它:

    SELECT *
    FROM   (
      SELECT s.studentid,
             c.classid,
             COALESCE( TO_CHAR( r.grade ), 'Not taken' ) AS grade
      FROM   students s
             CROSS JOIN classes c
             LEFT OUTER JOIN registration r
             ON ( s.studentid = r.studentid AND c.classid = r.classid )
    )
    PIVOT ( MAX( grade ) FOR classid IN (
      1 AS Class1,
      2 AS Class2,
      3 AS Class3
    ) )
    ORDER BY StudentID
    

    哪些输出:

    学生证 |一级 | 2 级 | CLASS3 :-------- | :-------- | :-------- | :-------- 001 | 4 | 3 | 1 002 | 2 |未采取 |不采取 003 | 5 |未采取 |不采取 004 |未采取 |未采取 |不采取 005 |未采取 |未采取 |不采取

    db小提琴here

    【讨论】:

    • 现象级的答案......有很好的例子。谢谢
    【解决方案2】:

    这只是条件聚合:

    select s.studentid,
           max(case when r.classid = 1 then r.grade end) as class1_grade,
           max(case when r.classid = 2 then r.grade end) as class2_grade,
           . . .
    from students s left join
         registrations r
         on r.studentid = s.studentid;
    

    您必须明确列出列。为避免这种情况,您需要动态 SQL (execute immediate)。

    以每行一个成绩获得结果更简单。使用cross join 生成行,使用left join 引入值:

    select s.studentid, c.classid, r.grade
    from students s cross join
         classes c left join
         registrations r
         on r.studentid = s.studentid and r.classid = c.classid;
    

    【讨论】:

      猜你喜欢
      • 2019-12-16
      • 1970-01-01
      • 2010-10-15
      • 2010-09-14
      • 1970-01-01
      • 2021-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多