關於Oracle中in,exists 與 not in, not exists


 文章簡要的討論了in,exists 與 not in, not exists在使用中的問題,主要是關鍵字的選擇,SQL的優化

*注:下面示例都是用Oracle內置用戶的表,如果安裝Oracle時沒有選擇不安裝數據庫示例表應該都會安裝的

1、IN和EXISTS

IN語句:

SELECT *
FROM hr.employees t1
WHERE t1.employee_id IN (
                         SELECT t2.employee_id
                         FROM hr.job_history t2                        
                        );

EXISTS語句:

SELECT *
FROM hr.employees t1
WHERE EXISTS (
              SELECT 1
              FROM hr.job_history t2
              WHERE t2.employee_id = t1.employee_id                      
             );

  可以看到兩者的結果是一樣的,這意味着兩個查詢都能夠滿足我們業務的需求。但是問題來了,那個以查詢更快呢?

  用in和exists都可以實現對數據的選擇,但是兩者的效率往往會因為場景不同而不同。原因如下:

in是把主表和子查詢的表作hash連接;而exists是對主表作loop循環,每次loop循環再對內表進行查詢。所以我們一直以來認為exists比in效率高的說法是不准確的。如果查詢的兩個表大小相當,那么用in和exists差別不大;如果兩個表中一個較小一個較大,則子查詢表大的用exists,子查詢表小的用in;

即:表A(小表),表B(大表)

select * from A where cc in(select cc from B)  -->效率低,用到了A表上cc列的索引;

select * from A where exists(select cc from B where cc=A.cc)  -->效率高,用到了B表上cc列的索引。

相反的:

select * from B where cc in(select cc from A)  -->效率高,用到了B表上cc列的索引

select * from B where exists(select cc from A where cc=B.cc)  -->效率低,用到了A表上cc列的索引。

 

2、NOT IN和NOT EXISTS

NOT IN語句:

SELECT *
FROM HR.EMPLOYEES T1
WHERE T1.EMPLOYEE_ID NOT IN (
                             SELECT T2.EMPLOYEE_ID 
                             FROM HR.JOB_HISTORY T2
                            );

NOT EXISTS語句:

SELECT *
FROM HR.EMPLOYEES T1
WHERE NOT EXISTS (
                  SELECT 1
                  FROM HR.JOB_HISTORY T2
                  WHERE T2.EMPLOYEE_ID = T1.EMPLOYEE_ID
                 );

  not in,not exists的對比與in,exists有比較大的不同,原因在於:

  如果查詢語句使用了not in,那么對主表,子查詢表都進行全表掃描沒有用到索引;而not exists的子查詢依然能用到表上的索引。所以無論哪個表大,用not exists都比not in 要快。

  而且坑爹的事情還沒有這么快就結束!

再演示一個比較坑爹的事情

--構造臨時表tmp1
WITH tmp1 AS (
SELECT 1 AS field1,2 AS field2 FROM dual UNION ALL
SELECT 1 AS field1,3 AS field2 FROM dual
),--多個with as用逗號隔開
--構造臨時表tmp2
tmp2 AS (
SELECT 1 AS field1,2    AS field2 FROM dual UNION ALL
SELECT 1 AS field1,NULL AS field2 FROM dual
)
SELECT *
FROM tmp1 t1
WHERE NOT EXISTS (
                  SELECT 1
                  FROM tmp2 t2
                  WHERE t1.field2 = t2.field2
                 );

結果如下:

沒有什么異常,但是用 not in的話坑爹的事情就會出現了!

--構造臨時表tmp1
WITH tmp1 AS (
SELECT 1 AS field1,2 AS field2 FROM dual UNION ALL
SELECT 1 AS field1,3 AS field2 FROM dual
),--多個with as用逗號隔開
--構造臨時表tmp2
tmp2 AS (
SELECT 1 AS field1,2    AS field2 FROM dual UNION ALL
SELECT 1 AS field1,NULL AS field2 FROM dual
)
SELECT * 
FROM tmp1 t1
WHERE t1.field2 NOT IN (
                        SELECT t2.field2
                        FROM tmp2 t2
                       );

結果如下:

WTF!!!!!!!

為什么會不同??????

原來使用not in時,它會調用子查詢;而使用not exists時,它會調用關聯子查詢。如果子查詢中返回的任意一條記錄含有空值,則查詢將不返回任何記錄。這就是導致我們上述問題的原因,所以一般情況下,我們都會用not exists而不用not in


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM