Oracle - 查找丢失/未连接的记录

Oracle - Finding missing /non-joined records

我在 Oracle 12 中遇到了一个问题,这个问题最容易用学生 classes 的传统数据库设计场景和参加 classes 的学生称为注册来解释。我很了解这个模型。我有一个场景,我需要得到一个完整的列表,所有学生反对所有 classes,以及他们是否正在接受 class 或不...

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

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 个不同的 classes...并且注册 table 将显示有多少学生参加了多少 classes。 .. 我需要的是信息加上所有未采用的组合...即我需要一份显示所有可能组合的报告(SQL 语句)...即 300 x 15,然后该行是否存在在注册中 table...例如,输出应如下所示...

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

如何填写缺失的数据,即学生和 class未被录取的学生的组合...?

这只是条件聚合:

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;

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;

然后输出:

STUDENTID | CLASSID | GRADE    
:-------- | :------ | :--------
001       | 1       | 4        
002       | 1       | 2        
003       | 1       | 5        
001       | 2       | 3        
001       | 3       | 1        
005       | 1       | Not taken
004       | 2       | Not taken
003       | 3       | Not taken
005       | 3       | Not taken
005       | 2       | Not taken
002       | 2       | Not taken
003       | 2       | Not taken
004       | 1       | Not taken
002       | 3       | Not taken
004       | 3       | Not taken

如果你想旋转它,那么:

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

输出:

STUDENTID | CLASS1    | CLASS2    | CLASS3   
:-------- | :-------- | :-------- | :--------
001       | 4         | 3         | 1        
002       | 2         | Not taken | Not taken
003       | 5         | Not taken | Not taken
004       | Not taken | Not taken | Not taken
005       | Not taken | Not taken | Not taken

db<>fiddle here