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
我在 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