【问题标题】:UNION ALL Performance IN SQL Server 2005SQL Server 2005 中的 UNION ALL 性能
【发布时间】:2010-02-05 08:52:29
【问题描述】:

我有一个以 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

【问题讨论】:

  • 如果重复运行相同的查询,这些执行时间会改变吗?
  • 是的,但不是很明显。无论如何差异大约是 10 倍
  • 根据您提供的额外信息,请参阅我的回答中的更新 2

标签: sql sql-server-2005 performance union


【解决方案1】:

确保在每次测试运行之间清除执行 + 数据缓存。

例如

DBCC FREEPROCCACHE
DBCC DROPCLEANBUFFERS

如果您先使用 UNION ALL 运行,然后再分别运行 2 个选择,则数据将已经缓存在内存中,从而提高性能(因此给人一种错误印象,即后续方法可能不会更快)。

如果您使用了 UNION,那么它可能会更慢,因为它必须应用 DISTINCT,但 UNION ALL 不必这样做,所以应该没有什么不同。

更新:
查看执行计划并进行比较 - 看看是否有任何区别。运行查询前,可以在SSMS中点击“Include Actual Execution Plan”按钮查看执行计划

更新 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 进行连接。

【讨论】:

  • 我多次尝试执行查询。执行时间变化不大。
  • 已添加更新。此外,您是在比较性能时只运行 2 个 SELECT,还是运行您提到的整个查询(有许多 CTE)?即它可能是查询中的其他内容,而不是这个特定的 UNION ALL 部分
  • 执行计划很大。我根本看不懂……但是有区别:当查询运行速度快时,它在运行速度慢时使用“合并连接”-“嵌套循环”。这是什么意思?我该怎么做才能让它使用相同的计划?
  • 您能否澄清您的问题:tDictionaryStreets 和 tDictionaryRegions 是 CTE 吗?如果是这样,您能否包括这些的全部详细信息。我不认为 UNION ALL 是造成明显性能差异的原因 - 我们需要看到更大的图景。
【解决方案2】:

你能比较一下执行计划吗?有什么不同吗? “Union all”应该可以正常工作,因为没有重复删除(这需要排序,这对于大量数据集来说成本很高)。

【讨论】:

  • 执行计划很大。当查询快速运行时,它使用“合并连接”,当查询运行缓慢时 - “嵌套循环”。这是什么意思?
【解决方案3】:

可能是网络(不太可能)或内存。取决于每个结果集带回的行数。 检查它是网络还是服务器的一种方法是在 SSMS 中包含客户端统计信息(查询 - 包括客户端统计信息 - SHIFT-ALT-S)。在底部,您可以区分大部分时间都花在了哪里。

你能比较一下执行计划吗? [...] lmsasu [...]查询运行速度快时使用“合并连接”,速度慢时使用“嵌套循环”。[...]

尚不能评论,但您在执行计划中看到的是“连接”两个结果集(合并连接)和 RBAR(发音为 reebar - Row By Agonizing Row [Jeff Moden])操作之间的区别,通常称为一个循环。

Merge Join: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'

【讨论】:

    【解决方案4】:

    我偶然发现了一个类似的问题,在仔细分析情况后,在我看来,在 UNION ALL 查询中使用 cte 会关闭并行化(这很可能是一个错误)。

    换句话说,UNION ALL 将等于两个查询的总和,其中每个查询都设置为 (maxdop 1)。

    虽然必须进行更多测试,并且实际上很难进行使用并行化来测试甚至作为错误提交给 Microsoft Connect 的查询,但您的问题以及中描述的问题仍然存在Why CTE (recurisve) is not parallilized (MAXDOP=8)? 也证明确实存在这样的问题。

    编辑:我已经进行了更广泛的测试,虽然 UNION ALL 确实并行化了很多次,但仍然存在没有 UNION ALL 它正在并行化但有一个 UNION ALL 将其关闭的情况。

    虽然这可能是一个错误,但也可能是查询优化器不寻找最佳计划而是寻找良好的结果> 计划,并且由于使用 UNION 连接的两个查询已经生成了复杂的计划以及带有 CTE 的查询,它甚至可能在考虑并行化选项之前就找到一个好的计划。

    【讨论】:

      猜你喜欢
      • 2010-09-14
      • 1970-01-01
      • 1970-01-01
      • 2015-03-03
      • 2018-07-11
      • 2012-09-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多