2017-10-06 78 views
0

我有連接使用該條件的表的查詢:ISNULL與「IS NULL和字段= 0」

(Apps.mfrId = Manufacturer.id OR Apps.mfrId IS NULL OR Apps.mfrId = 0) 

查詢需要17秒至運行,並且使用分析的查詢導致5之間 - 30條消息(它會改變每次運行),錯誤爲「錯誤:1222,嚴重性:16,狀態:18」。

我離開查詢完全一樣,但我改變上述條件爲:

(Apps.mfrId = Manufacturer.id OR ISNULL(apps.mfrId, 0) = 0) 

...現在這一個變化相同的查詢在140ms的運行,並沒有鎖定的錯誤。

爲什麼會發生這種情況?

請注意,在對錶製造商和應用程序進行測試之前,我已經運行帶有repair_bebuild選項的DBCC CHECKTABLE,並且還重建了兩個表上的索引。

另請注意,沒有其他查詢在同一時間對數據庫運行。

這裏是一個有錯誤的查詢的簡化版本:

select top 2000 Apps.object_id 
from 
    Manufacturer 
    INNER JOIN Apps ON (
     Apps.mfrId = Manufacturer.Id 
     OR Apps.mfrId IS NULL 
     OR Apps.mfrId = 0 
    ) 
where 
    Apps.OBJECT_ID = 6879149 

如果不是「頂2000」我使用「1000強」的查詢,在短短大於100ms完成。

+0

您是否顯示每個查詢的執行計劃? –

+0

連接條件看起來非常奇怪。這是一個左連接,內連接,?我感興趣的是爲什麼你想要一個連接條件,如果apps.mfrid爲空將被覆蓋。聽起來像你只是想完全加入。什麼是最終目標? – scsimon

+0

@scsimon這是一個內部連接。我正在使用我沒有設計的桌子。 Apps.mfrId是Manufacturer.id的空(null)外鍵(沒有外鍵約束設置)。出於某種原因,原始設計允許NULL或0,並且NULL或0具有相同的含義(即沒有設置有效值)。 –

回答

0

如果Apps.mfrId爲null,那麼您將加入每個製造商。

從您的測試, 「OR ISNULL(apps.mfrId,0)= 0」 確實比這個更便宜的 「OR Apps.mfrId爲空或Apps.mfrId = 0」

這些做似乎很等價的,你已經有了一個解決方案,所以問題是爲什麼和/或如何讓非表演者工作。

說到這樣的表現,如果你說的一切都是正確的,當我們開始問爲什麼時,我們開始指向查詢優化器。如果它對於相同的參數表現不同,它將有不同的查詢計劃。您可能會看到表掃描而不是索引使用情況,或者對性能不佳的其他解釋。

我鼓勵你比較一下查詢計劃,或者讓別人來幫助引入一個關於它在做什麼不同的答案。但是,你已經可以告訴它正在做一些不同的事情。

一種可能性是,當查詢優化器修復被釋放,他們在默認情況下不開啓,除非標誌4199(所有修正或其他標誌的具體修正)被開啓。這是因爲一般修補程序對於大多數人來說可能是好的,但是也可能會破壞在現有的怪癖環境中進行了優化的應用程序。

https://dba.stackexchange.com/questions/102292/trace-flag-4199-enable-globally

是否在幫助開啓4199?

select top 2000 Apps.object_id 
from 
    Manufacturer 
    INNER JOIN Apps ON (
     Apps.mfrId = Manufacturer.Id 
     OR Apps.mfrId IS NULL 
     OR Apps.mfrId = 0 
    ) 
where 
    Apps.OBJECT_ID = 6879149 
OPTION(QUERYTRACEON 4199) 

另一個相關的議題是參數嗅探。有時候查詢計劃可能會被緩存,這對於一個參數是最佳的,但是對於另一個參數是可怕的。在你的情況下,一個應用程序可能會返回1個製造商,但另一個應用程序可能會返回所有制造商,這就是值得一提的原因。這通常顯示爲相同的代碼在給定不同參數時不一致地執行不良。 您可以嘗試關閉參數嗅探或強制重新編譯以幫助診斷問題是否成爲問題的一部分。

What are the main differences between OPTION(OPTIMIZE FOR UNKNOWN) and OPTION(RECOMPILE)?

我曾經見過,如果覺得查詢優化器的情況下已經放棄細讀指標,只是因爲該語句變得過於複雜。但在你的例子中這似乎不太可能;除了查詢優化器錯誤的問題得到解決之外,要利用它們,您仍然必須打開查詢標記,否則您安裝的補丁可能無法做任何事情。

有時您還可以嘗試幫助指導sql優化器。如果應該總是使用一個索引,那麼可以將其作爲查詢提示給出。

我也很好奇知道,如果以下刪除問題:如果所有的查詢優化器修復被打開

select top 2000 Apps.object_id 
from 
(
    select Apps.object_id, Apps.mfrId 
    from Apps 
    where Apps.OBJECT_ID = 6879149 
) Apps 
left join Manufacturer ON (
     Apps.mfrId = Manufacturer.Id 
     OR Apps.mfrId IS NULL 
     OR Apps.mfrId = 0) 

,問題仍然存在,我們要問,爲什麼查詢優化器做這個。它只能使用存在的索引,並且只有在存在統計信息的情況下才能確定使用索引是否有利。同時,過時的統計數據將導致不好的選擇。定期重建/重組索引並更新統計信息可能會很好。你可能想嘗試這樣做,看看它是否有任何影響。

結論

既然你已經有了一個固定的工作,不要使用它。但是你的問題是爲什麼兩個非常相似的事情會達到非常不同的結果。假設正在使用相同的參數,並且包括兩個選項非常相似的事實,則問題指向查詢優化器是一個不好的選擇。這表明您可能需要安裝最新的修補程序(如果未安裝),並且還啓用4199以實際啓用您已通過sql server修補程序安裝的所有查詢優化程序修補程序。這包括將OPTION(QUERYTRACEON 4199)添加到sql的底部,或者在全局啓用4199或類似的。

+0

想法是這樣的。我們有一個應用程序(一個應用程序是在車輛上使用零件的地方)。如果應用程序有製造商設置,那麼該車輛與該製造商相關聯。如果車輛沒有製造商設置(其mfrid爲0或null),則它與所有制造商相關聯。 –

+0

是的,但爲什麼ISNULL的變化要快幾百倍,爲什麼當其他版本的查詢得到5 - 30 1222錯誤時,爲什麼不會得到大量的錯誤1222。 –

+0

雅,我已經通過優化器運行它,並使用SQL管理工作室中的比較選項來比較執行計劃。我得到了它展示給我的概念,但在這種情況下對我來說並不是非常有用。 –