Performance

使用 WHERE NOT IN 子選擇子句提高性能

  • July 8, 2021

在以下查詢中,我必須計算每個客戶的交易。但是,我必須從結果集中完全排除交易超過一年的客戶。

查詢優化器不應該足夠聰明,只為每個客戶評估一次存在嗎?

--Count transactions on customers that are less than 1 year old

SELECT t1.CUSTID, COUNT(*)
FROM CUST_TRX t1
WHERE NOT EXISTS ( 
 SELECT FIRST 1 1 
 FROM CUST_TRX t2 
 WHERE 
   t2.CUSTID = t1.CUSTID AND
   t2.DATED < CURRENT_DATE - 365
 GROUP BY t2.CUSTID
)
GROUP BY t1.CUSTID

我的查詢計劃中沒有自然。此查詢的執行就像數據庫為每個事務執行存在子句,而不是為每個客戶執行它。GROUP BY如果我刪除子查詢中的 ,性能是相同的。

有沒有更好的方法來做到這一點,以便我可以從數據庫中獲得更好的性能?如果可能的話,希望一個簡單的SELECT查詢能夠避免 CTE(這會帶來其他挑戰)。

由於其他GROUP BY條件(此處未顯示),我無法簡單地檢查MIN(DATED),我真的需要執行另一個查詢。

LEFT OUTER JOIN對於這樣的查詢,執行 a而不是NOT EXISTS樣式檢查通常更有效,它通常意味著完整的索引掃描(或沒有正確索引的表掃描)但是在主表中有很多行,這會更少比大量的索引查找(主表返回的每一行的參考表上的一個)昂貴,否則會導致。一些查詢計劃者非常善於發現這種等價性並使用替代計劃,這是更好的選擇,但在您的情況下聽起來並沒有發生這種情況。

嘗試類似:

SELECT t1.CUSTID, COUNT(*)
FROM   CUST_TRX t1
LEFT OUTER JOIN
      CUST_TRX t2 
ON     t2.CUSTID=t1.CUSTID 
AND    t2.DATED<CURRENT_DATE-365
WHERE  t2.CUSTID IS NULL
GROUP BY t1.CUSTID

(注意:我不熟悉火鳥,所以上面的語法可能需要調整,但應該說明這一點)

如果沒有匹配中的每一WHERE t2.CUSTID IS NULL行,將為找到的每個匹配輸出一次,而沒有匹配的行將輸出一次,但從該對像中選擇的任何列都設置為 NULL。然後該子句篩選出匹配項。t1``t2``t2``t2``WHERE

取決於數據庫引擎的能力,特別是如果參考對象(CUST_TRX此處應用了過濾器)中的數據量很大,這可能WHERE <something> NOT INor選項效率低得多WHERE NOT EXISTS,因此在使用該方法之前首先對實際數據集進行基準測試。在查詢計劃者沒有註意到WHERE NOT IN可以更有效地執行這種安排的情況下,它通常與 MS SQL Server 一起工作效率更高。

此外,如果您這樣做,請在程式碼(和/或支持文件)中留下評論,說明您這樣做是等效的,WHERE <something> NOT IN或者WHERE NOT EXISTS您希望更有效。您會記住它,並且有經驗的 SQL 人員會辨識該模式,但是查看程式碼的其他人可能不會立即理解其意圖/原因並將其轉回使用WHERE NOT EXISTS,因為這樣讀起來比英文句子更好。

引用自:https://dba.stackexchange.com/questions/48830