原文地址:https://blog.csdn.net/weixin_46156257/article/details/131234451
SQL的表連線中,如果主表中同一條資料對應被連線表有多條資料,則連線後資料會被擴大,但有時候我們希望資料不要被擴大,與主表中資料條數保持一致,即實現左單一匹配連線。
假設我們有學生資訊表TEST_TAB_STUDENTS和學生成績表TEST_TAB_SCORE,如下:
TEST_TAB_STUDENTS:
NAME | STU_NUMBER | CLASS | GRADE |
張三 | 23578 | 三班 | 五年級 |
李四 | 23632 | 二班 | 五年級 |
王五 | 23448 | 一班 | 六年級 |
趙六 | 32251 | 三班 | 四年級 |
TEST_TAB_SCORE:
如果需要將學生表中匹配出每個學生的任意一筆成績(或按一定順序一條),則需要用到左單一匹配。
先將被連線表排序後取一條(如果是任意一條則可以寫order by 1)
SELECT A.NAME,A.STU_NUMBER,A.CLASS,A.GRADE,C.MONTH_TIMEKEY,C.CHINESE,C.MATHS,C.ENGLISH,C.PHYSICS,C.CHEMISTRY,C.BIOLOGY FROM TEST_TAB_STUDENTS A --如果需要按某一列排序,例如取時間最後的一條成績,可改為ORDER BY MONTH_TIMEKEY DESC LEFT JOIN (SELECT B.*,ROW_NUMBER() OVER (PARTITION BY NAME ORDER BY 1) RN FROM TEST_TAB_SCORE B) C ON A.NAME = C.NAME WHERE C.RN = 1
輸出:
2. 透過hint:first_rows查詢被連線表中按一定順序後的第一條(或任意一條)
如果連線後只需查詢一列,如語文成績,則可直接查詢:
1 --根據MONTH_TIMEKEY排序取最後一條資料 2 SELECT A.NAME,A.STU_NUMBER,A.CLASS,A.GRADE, 3 (SELECT /*+ FIRST_ROWS*/CHINESE 4 FROM 5 (SELECT CHINESE 6 FROM TEST_TAB_SCORE B 7 WHERE A.NAME = B.NAME 8 ORDER BY MONTH_TIMEKEY DESC 9 ) WHERE ROWNUM<=1 10 ) AS CHINESE 11 FROM TEST_TAB_STUDENTS A 12 13 --不需要排序取任意一條 14 SELECT A.NAME,A.STU_NUMBER,A.CLASS,A.GRADE, 15 (SELECT /*+ FIRST_ROWS*/CHINESE 16 FROM TEST_TAB_SCORE B 17 WHERE A.NAME = B.NAME 18 AND ROWNUM<=1 19 ) AS CHINESE 20 FROM TEST_TAB_STUDENTS A
如果連線後需要的列較多,如各科成績都需要,則重複查詢會耗費效能,可以先查詢出ROWID,再左連線:
1 --根據MONTH_TIMEKEY排序取最後一條資料 2 SELECT AA.NAME,AA.STU_NUMBER,AA.CLASS,AA.GRADE,C.MONTH_TIMEKEY,C.CHINESE,C.MATHS,C.ENGLISH,C.PHYSICS,C.CHEMISTRY,C.BIOLOGY 3 FROM ( 4 SELECT A.NAME,A.STU_NUMBER,A.CLASS,A.GRADE, 5 (SELECT /*+ FIRST_ROWS*/ROW_ID 6 FROM 7 (SELECT ROWID ROW_ID 8 FROM TEST_TAB_SCORE B 9 WHERE A.NAME = B.NAME 10 ORDER BY MONTH_TIMEKEY DESC 11 ) WHERE ROWNUM<=1 12 ) AS ROW_ID 13 FROM TEST_TAB_STUDENTS A 14 ) AA 15 LEFT JOIN TEST_TAB_SCORE C ON AA.ROW_ID = C.ROWID 16 17 --不需要排序取任意一條 18 SELECT AA.NAME,AA.STU_NUMBER,AA.CLASS,AA.GRADE,C.MONTH_TIMEKEY,C.CHINESE,C.MATHS,C.ENGLISH,C.PHYSICS,C.CHEMISTRY,C.BIOLOGY 19 FROM ( 20 SELECT A.NAME,A.STU_NUMBER,A.CLASS,A.GRADE, 21 (SELECT /*+ FIRST_ROWS*/ ROWID ROW_ID 22 FROM TEST_TAB_SCORE B 23 WHERE A.NAME = B.NAME 24 AND ROWNUM<=1 25 ) AS ROW_ID 26 FROM TEST_TAB_STUDENTS A 27 ) AA 28 LEFT JOIN TEST_TAB_SCORE C ON AA.ROW_ID = C.ROWID
3. 兩種方法比較
兩種方式均能實現需求,在不同場景效能表現有所不同:
方法1採用排序後hash join的方式,適用於主表(TEST_TAB_STUDENTS)較大,而被匹配表(TEST_TAB_SCORE)較小的情況,因為被匹配表需要全部排序;方法2則採用first_row方式迭代取出B表的第一條資料,適用於主表(取出的資料)較小,而B表較大的情況。