[英]Oracle - Finding missing /non-joined records
我在 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
我如何填寫缺失的數據,即未參加的學生和班級的組合...?
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;
然后輸出:
\n學生證 | 經典 | 年級 \n :-------- | :------ | :--------\n 001 | 1 | 4 \n 002 | 1 | 2 \n 003 | 1 | 5 \n 001 | 2 | 3 \n 001 | 3 | 1 \n 005 | 1 | 不采取\n 004 | 2 | 不采取\n 003 | 3 | 不采取\n 005 | 3 | 不采取\n 005 | 2 | 不采取\n 002 | 2 | 不采取\n 003 | 2 | 不采取\n 004 | 1 | 不采取\n 002 | 3 | 不采取\n 004 | 3 | 不采取\n
如果你想旋轉它,那么:
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
哪些輸出:
\n學生證 | 1 級 | 2 級 | 3級 \n :-------- | :-------- | :-------- | :--------\n 001 | 4 | 3 | 1 \n 002 | 2 | 未采取| 不采取\n 003 | 5 | 未采取| 不采取\n 004 | 未采取| 未采取| 不采取\n 005 | 未采取| 未采取| 不采取\n
db<> 在這里擺弄
這只是條件聚合:
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;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.