簡體   English   中英

UNION ALL SQL Server 2005中的性能

[英]UNION ALL Performance IN SQL Server 2005

我有一個以長鏈CTE結尾的查詢

SELECT RegionName, AreaName, CityName, SubCityName, StreetName 
FROM tDictionaryStreets
UNION ALL
SELECT RegionName, AreaName, CityName, SubCityName, StreetName 
FROM tDictionaryRegions

此查詢的執行時間為1450毫秒。 當我分別執行這兩個SELECT時,它花費的時間要少得多。 對於查詢

SELECT RegionName, AreaName, CityName, SubCityName, StreetName 
FROM tDictionaryStreets

執行時間是106毫秒。 並為查詢

SELECT RegionName, AreaName, CityName, SubCityName, StreetName 
FROM tDictionaryRegions

這是20毫秒。

為什么UNION ALL會將執行時間增加10倍以上? 我該怎么做才能減少它?

謝謝您的幫助。

更新整個查詢(我縮短了,但問題仍然存在)是

WITH tFoundRegions AS
(
    SELECT KladrItemName FROM dbo.tBuiltKladrItemsWithQuants
    WHERE UserID = @UserID AND (indeces & 1) > 0
),
tFoundAreas AS
(
    SELECT KladrItemName FROM dbo.tBuiltKladrItemsWithQuants
    WHERE UserID = @UserID AND (indeces & 2) > 0
),
tFoundCities AS
(
    SELECT KladrItemName FROM dbo.tBuiltKladrItemsWithQuants
    WHERE UserID = @UserID AND (indeces & 4) > 0
),
tFoundSubCities AS
(
    SELECT KladrItemName FROM dbo.tBuiltKladrItemsWithQuants
    WHERE UserID = @UserID AND (indeces & 8) > 0
),
tFoundStreets AS
(
    SELECT KladrItemName FROM dbo.tBuiltKladrItemsWithQuants
    WHERE UserID = @UserID AND (indeces & 16) > 0
),
tDictionaryStreets AS
(
    SELECT DISTINCT
        CASE WHEN RegionName IN (SELECT KladrItemName FROM tFoundRegions) THEN RegionName ELSE NULL END RegionName
      , CASE WHEN AreaName IN (SELECT KladrItemName FROM tFoundAreas) THEN AreaName ELSE NULL END AreaName
      , CASE WHEN CityName IN (SELECT KladrItemName FROM tFoundCities) THEN CityName ELSE NULL END CityName
      , CASE WHEN SubCityName  IN (SELECT KladrItemName FROM tFoundSubCities) THEN SubCityName ELSE NULL END SubCityName
      , StreetName 
    FROM StreetNames
    WHERE StreetName IN (SELECT KladrItemName FROM tFoundStreets)
),
tMissingSubCities AS
(
    SELECT KladrItemName FROM tFoundSubCities
    WHERE KladrItemName NOT IN (SELECT SubCityName FROM tDictionaryStreets)
),
tDictionarySubCities AS
(
    SELECT DISTINCT 
        CASE WHEN RegionName IN (SELECT KladrItemName FROM tFoundRegions) THEN RegionName ELSE NULL END RegionName
      , CASE WHEN AreaName IN (SELECT KladrItemName FROM tFoundAreas) THEN AreaName ELSE NULL END AreaName
      , CASE WHEN CityName IN (SELECT KladrItemName FROM tFoundCities) THEN CityName ELSE NULL END CityName
      , SubCityName
      , NULL StreetName 
    FROM SubCityNames
    WHERE SubCityName IN (SELECT KladrItemName FROM tMissingSubCities)
)
SELECT RegionName, AreaName, CityName, SubCityName, StreetName 
FROM tDictionaryStreets
UNION ALL
SELECT RegionName, AreaName, CityName, SubCityName, StreetName 
FROM tDictionarySubCities

確保清除每次測試運行之間的執行+數據緩存。

例如

DBCC FREEPROCCACHE
DBCC DROPCLEANBUFFERS

如果先使用UNION ALL運行,然后再單獨運行2個選擇,則數據已經緩存在內存中,使性能更好(因此給人以錯誤的印象,即后續方法可能更快)。

如果您使用了UNION,那么它可能會變慢,因為它必須應用DISTINCT,但UNION ALL不必這樣做,所以它應該沒有什么不同。

更新:
看看執行計划並比較它們 - 看看是否有任何區別。 在運行查詢之前,可以通過單擊SSMS中的“包括實際執行計划”按鈕來查看執行計划

更新2:
基於完整的CTE,我想我會考慮優化它們 - 我認為UNION ALL實際上並不是問題。

恕我直言,最好的嘗試是逐個完成CTE,並嘗試單獨優化每個,以便當您在主查詢中將它們全部組合時,它們表現更好。

例如對於tDictionaryStreets,嘗試這個怎么樣:

SELECT DISTINCT
    r.KladrItemName AS RegionName,
        a.KladrItemName AS AreaName,
        c.KladrItemName AS CityName,
        sc.KladrItemName AS SubCityName,
        s.StreetName      
FROM StreetNames s
    JOIN tFoundStreets fs ON s.StreetName = fs.KladrItemName
    LEFT JOIN tFoundRegions r ON s.RegionName = r.KladrItemName
    LEFT JOIN tFoundAreas a ON s.AreaName = a.KladrItemName
    LEFT JOIN tFoundCities c ON s.CityName = c.KladrItemName
    LEFT JOIN tFoundSubCities sc ON s.SubCityName = scc.KladrItemName

每個表上的KladrItemName至少應該有一個索引。 嘗試以與連接相同的方式重新處理tDictionarySubCities。

你能比較執行計划嗎? 有什么不同嗎? “Union all”應該可以正常工作,因為沒有重復刪除(這需要排序,這對於大型數據集來說是昂貴的)。

可能是網絡(不太可能)或內存。 取決於每個結果集帶回的行數。 檢查是網絡還是服務器的一種方法是在SSMS中包含客戶端統計信息(查詢 - 包括客戶端統計信息 - SHIFT-ALT-S)。 在底部,您可以區分大部分時間花在哪里。

你能比較執行計划嗎? [...] lmsasu [...]當查詢快速運行時,它使用'merge join',當慢 - '嵌套循環'。[...]

暫不公開,但你在執行計划中看到的是“加入”兩個結果集(合並連接)和RBAR(發音為reebar - Row By Agonizing Row [Jeff Moden])操作之間的區別,通常稱為循環。

合並連接:SQL使用公共鏈接查找兩個結果集,並執行基於集合的操作以將兩個集合在一起。 嵌套循環:SQL無法找到公共鏈接,並將第1組中的一行連接到第2組中的所有行,並丟棄那些不匹配的行。

直覺上,SQL偶然發現了NULL結果,這些結果都是未知結果。 嘗試分配一個像“XYZ”(或任何已知的未出現的)的值,您可以在上一個查詢中進行簡單過濾。 這可能會避免某些結果集中的嵌套循環,因為值已確定且未知。 相近:

[...]
tDictionarySubCities AS 
( 
    SELECT DISTINCT  
        CASE WHEN RegionName IN (SELECT KladrItemName FROM tFoundRegions) THEN RegionName ELSE 'XYZXYZ' END RegionName 
      , CASE WHEN AreaName IN (SELECT KladrItemName FROM tFoundAreas) THEN AreaName ELSE 'XYZXYZ' END AreaName 
      , CASE WHEN CityName IN (SELECT KladrItemName FROM tFoundCities) THEN CityName ELSE 'XYZXYZ' END CityName 
      , SubCityName 
      , NULL StreetName  
    FROM SubCityNames 
    WHERE SubCityName IN (SELECT KladrItemName FROM tMissingSubCities) 
) 
SELECT RegionName, AreaName, CityName, SubCityName, StreetName  
FROM tDictionaryStreets 
WHERE RegionName <> 'XYZ'
UNION ALL 
SELECT RegionName, AreaName, CityName, SubCityName, StreetName  
FROM tDictionarySubCities 
WHERE RegionName <> 'XYZ'

我偶然發現了類似的問題,經過仔細分析后的情況,在我看來,在UNION ALL查詢中使用cte會關閉並行化(這很可能是一個錯誤)。

換句話說,UNION ALL將等於兩個查詢的總和,其中每個查詢都設置為(maxdop 1)。

雖然必須進行更多測試,但實際上很難做出使用並行化的查詢,以便能夠測試或甚至作為錯誤提交給Microsoft Connect,仍然是您的問題以及為什么CTE中描述的問題( recurisve)不是平行的(MAXDOP = 8)? 也證明實際上存在這樣的問題。

編輯:我已經進行了更廣泛的測試,雖然UNION ALL多次並行化,但仍有一些情況,如果沒有UNION ALL,它會並行化,但有一個UNION ALL將其關閉。

雖然這可能是一個錯誤,但也可能是因為查詢優化器沒有尋找最佳計划,而是尋找一個好的計划,並且因為與UNION連接的兩個查詢已經生成了復雜的計划,以及使用CTE的查詢,它甚至可以在考慮並行化選項之前找到一個好的計划。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM