EXISTS (SELECT 1 …) vs EXISTS (SELECT * …) 一個還是另一個?
每當我需要檢查表中是否存在某行時,我傾向於始終編寫如下條件:
SELECT a, b, c FROM a_table WHERE EXISTS (SELECT * -- This is what I normally write FROM another_table WHERE another_table.b = a_table.b )
其他一些人這樣寫:
SELECT a, b, c FROM a_table WHERE EXISTS (SELECT 1 --- This nice '1' is what I have seen other people use FROM another_table WHERE another_table.b = a_table.b )
當條件
NOT EXISTS
不是EXISTS
: 在某些情況下,我可能會用 aLEFT JOIN
和一個額外的條件(有時稱為antijoin)來編寫它:SELECT a, b, c FROM a_table LEFT JOIN another_table ON another_table.b = a_table.b WHERE another_table.primary_key IS NULL
我盡量避免使用它,因為我認為含義不太清楚,特別是當您
primary_key
的內容不那麼明顯時,或者當您的主鍵或連接條件是多列時(您很容易忘記其中一列)。但是,有時您維護由其他人編寫的程式碼……它就在那裡。
- 有什麼區別(除了風格)來
SELECT 1
代替SELECT *
嗎?有沒有表現不同的極端情況? 2. 雖然我寫的是(AFAIK)標準SQL:不同的數據庫/舊版本有這樣的區別嗎? 3. 明確寫反加入有什麼好處嗎?
當代計劃者/優化者是否將其與
NOT EXISTS
條款區別對待?
(NOT) EXISTS (SELECT 1 ...)
不,(NOT) EXISTS (SELECT * ...)
所有主要 DBMS之間的效率沒有差異。我也經常看到(NOT) EXISTS (SELECT NULL ...)
被使用。在某些情況下,您甚至可以編寫
(NOT) EXISTS (SELECT 1/0 ...)
並且結果是相同的 - 沒有任何(除以零)錯誤,這證明那裡的表達式甚至沒有被評估。關於
LEFT JOIN / IS NULL
antijoin 方法,更正一下:這相當於NOT EXISTS (SELECT ...)
.在這種情況下,
NOT EXISTS
vsLEFT JOIN / IS NULL
,你可能會得到不同的執行計劃。例如,在 MySQL 中,主要是在舊版本(5.7 之前)中,計劃非常相似,但並不完全相同。據我所知,其他 DBMS(SQL Server、Oracle、Postgres、DB2)的優化器或多或少能夠重寫這兩種方法並為兩者考慮相同的計劃。儘管如此,仍然沒有這樣的保證,並且在進行優化時,最好檢查來自不同等效重寫的計劃,因為可能存在每個優化器不重寫的情況(例如,複雜查詢,具有許多連接和/或派生表/子查詢中的子查詢,其中來自多個表的條件、連接條件中使用的複合列)或優化器選擇和計劃受到可用索引、設置等的不同影響。另請注意,
USING
不能在所有 DBMS 中使用(例如 SQL Server)。比較常見的JOIN ... ON
作品無處不在。並且列需要以表名/別名為前綴,
SELECT
以避免在我們進行連接時出現錯誤/歧義。我通常也更喜歡將連接的列放在
IS NULL
檢查中(儘管 PK 或任何不可為空的列都可以,但當計劃LEFT JOIN
使用非聚集索引時,它可能對效率很有用):SELECT a_table.a, a_table.b, a_table.c FROM a_table LEFT JOIN another_table ON another_table.b = a_table.b WHERE another_table.b IS NULL ;
還有第三種反連接方法,
NOT IN
但是如果內部表的列可以為空,則使用它具有不同的語義(和結果!)。它可以通過排除帶有 的行來使用NULL
,使查詢等效於前兩個版本:SELECT a, b, c FROM a_table WHERE a_table.b NOT IN (SELECT another_table.b FROM another_table WHERE another_table.b IS NOT NULL ) ;
這通常也會在大多數 DBMS 中產生類似的計劃。