2012-06-25 117 views
4

我有一個需要很長時間才能完成的SQL查詢(對於SQL Server 2008 R2)。我想知道是否有更好的方法來做到這一點?這個SQL查詢可以優化運行得更快嗎?

SELECT @count = COUNT(Name) 
FROM Table1 t 
WHERE t.Name = @name AND t.Code NOT IN (SELECT Code FROM ExcludedCodes) 

表1中有大約90萬行,並按名稱和代碼索引。 ExcludedCodes只有大約30行。

這個查詢是在一個存儲過程中被調用了大約40k次,這個過程完成所花費的總時間是27分鐘。我相信這是我最大的瓶頸,因爲它查詢的行數很多,它做它的次數。

所以,如果你知道一個很好的方法來優化它,將不勝感激!如果它不能再優化,我猜IM堅持了27分鐘......

編輯

我改變了NOT INNOT EXISTS並削減時間到10:59,讓孤單是一個巨大的獲得我的份量。我仍然試圖按照下面的建議去做group by statement,但是這需要完全重寫存儲過程,並且可能需要一些時間......(正如我之前所說的,我不是SQL中最好的,但它正在啓動在我身上生長。^^)

+0

男人,你正在做一個90M-30行40000次的解析計數......你準確的預期:D – Sebas

+0

你是否考慮過使用JOINs? –

+0

是名稱唯一嗎? – Sebas

回答

5

除了變通方法來獲取查詢本身響應速度更快,你有沒有考慮維護,告訴它是否在表中的列這套還是不是?它需要大量的維護,但如果ExcludedCodes表不經常更改,則可能更好。例如,你可以添加一個BIT列:

ALTER TABLE dbo.Table1 ADD IsExcluded BIT; 

讓它NOT NULL和默認爲0,然後你可以創建一個過濾索引:

CREATE INDEX n ON dbo.Table1(name) 
    WHERE IsExcluded = 0; 

現在,你只需要一次更新表:

UPDATE t 
    SET IsExcluded = 1 
    FROM dbo.Table1 AS t 
    INNER JOIN dbo.ExcludedCodes AS x 
    ON t.Code = x.Code; 

並且正在進行中,您必須在兩個表上都使用觸發器來保持此狀態。有了這個地方,您的查詢就會變成:

SELECT @Count = COUNT(Name) 
    FROM dbo.Table1 WHERE IsExcluded = 0; 

編輯

至於「NOT IN比左慢JOIN」在這裏我只有幾千行進行一個簡單的測試:

enter image description here

編輯2

我不知道爲什麼這個查詢不會做你以後,並遠遠超過你的40K循環更有效:

SELECT src.Name, COUNT(src.*) 
    FROM dbo.Table1 AS src 
    INNER JOIN #temptable AS t 
    ON src.Name = t.Name 
    WHERE src.Code NOT IN (SELECT Code FROM dbo.ExcludedCodes) 
    GROUP BY src.Name; 

還是LEFT JOIN相當於:

SELECT src.Name, COUNT(src.*) 
    FROM dbo.Table1 AS src 
    INNER JOIN #temptable AS t 
    ON src.Name = t.Name 
    LEFT OUTER JOIN dbo.ExcludedCodes AS x 
    ON src.Code = x.Code 
    WHERE x.Code IS NULL 
    GROUP BY src.Name; 

我會花費不到27分鐘的時間。我甚至會建議按順序運行這兩個查詢將比您的一個需要27分鐘的查詢快得多。

最後,您可能會考慮索引視圖。我不知道你的表格結構,以及你是否違反了任何限制條件,但值得調查恕我直言。

+0

謝謝你的測試,我實際上在幾天前想知道我自己,但是用「NOT IN」選項代替。我猜是不錯的選擇。 ^^至於添加IsExcluded位,我不能改變我正在用不幸的工作表..我曾經想過做類似的事情,但它不是一個改變它的選項。 >< –

+0

#tempTable究竟是什麼?我正在翻閱你的答案和其他答案,甚至我自己的問題,我似乎無法把我的手指放在那張桌子上究竟是什麼.. –

+0

@BrandonStout你說:「它在一個非常糟糕的while循環中,我做了一次一行地在臨時表中循環一次「 - 是不是臨時表?它沒有存儲你需要加入的名字嗎?如果你顯示你的實際代碼而不是一部分,建議可能會更準確。 –

2

NOT EXISTS通常表現比NOT IN更好,但是你應該在你的系統上測試它。

SELECT @count = COUNT(Name) 
FROM Table1 t 
WHERE t.Name = @name AND NOT EXISTS (SELECT 1 FROM ExcludedCodes e WHERE e.Code = t.Code) 

不知道更多關於您的查詢,很難提供具體的優化建議(即適合複製/粘貼的代碼)。它真的需要運行40,000次嗎?聽起來像你的存儲過程需要重新工作,如果這是可行的。你可以在proc開始時執行上面的操作,並將結果插入臨時表中,該臨時表可以保留來自Table1的索引,然後加入,而不是運行此查詢。

這個特殊的位甚至可能不是使查詢運行27分鐘的瓶頸。例如,你是否在你的WHERE子句中使用了超過9000萬行或標量UDF的遊標?

+0

我會嘗試這個,讓你知道! (可能是一段時間..大聲笑) –

+1

當我編輯時,這個問題上的很多行動。我在HLGEM的回答中看到您正在使用while循環。努力解決這個問題,你幾乎肯定會從優化該循環中獲得更大的性能提升,而不是將'NOT IN'更改爲'NOT EXISTS'。 –

+0

不需要返回'e.code'可能會稍微快一點。所有需要的是'1'。我會編輯;隨時刪除。 – whytheq

1

你有沒有想過做一次查詢並填充表變量或臨時表中的數據?類似於

insert into #temp (name, Namecount) 
values Name, Count(name) 
from table1 
where name not in(select code from excludedcodes) 
group by name 

並且不要忘記,只要排除的代碼表有些靜態,您可以使用已篩選的索引。

+0

+1。我正準備發佈這樣的內容。我認爲,儘管OP可以嘗試優化查詢,但它運行40k次更是一個問題。 –

0

開始評估執行計劃。哪個是計算最重要的部分? 關於這兩個表之間的關係,請在索引列上使用JOIN:索引將優化查詢執行。

4

你說這被稱爲約40K次。爲什麼?它是否在遊標中?如果是這樣,你真的需要一個遊標。難道你不能把你想要的@name的值放在臨時表中,並將它編入索引,然後加入它?

select t.name, count(t.name) 
from table t 
join #name n on t.name = n.name 
where NOT EXISTS (SELECT Code FROM ExcludedCodes WHERE Code = t.code) 
group by t.name 

這可能會讓你在一個查詢中獲得所有結果,幾乎可以肯定比40K個別查詢快。當然,如果你需要的所有名的計數,它甚至simpleer

select t.name, count(t.name) 
    from table t 
NOT EXISTS (SELECT Code FROM ExcludedCodes WHERE Code = t 
group by t.name 
+0

沒有它沒有在遊標..它在一個非常糟糕的while循環我做了循環通過一張臨時表一行一次..我對這種方法感興趣,雖然我無法弄清楚如何獲得組正確工作..我要去看看這個有點謝謝! –

+0

雖然循環也通過激動行來運行。 – HLGEM

+0

@BrandonStout A while循環*是一個遊標。你爲什麼認爲它有什麼不同?請閱讀:http://sqlblog.com/blogs/aaron_bertrand/archive/2012/01/26/the-fallacy-that-a-while-loop-isn-ta-cursor.aspx ...當然有一套不需要40,000次迭代的解決方案。 –