【问题标题】:TSQL - How to avoid UNION ALLTSQL - 如何避免 UNION ALL
【发布时间】:2021-10-10 00:02:00
【问题描述】:

样本数据:

DECLARE @Parent TABLE
    (
        [Id] INT
      , [Misc_Val] VARCHAR(5)
    ) ;

DECLARE @Children TABLE
    (
        [Id]   INT
      , [P_ID] INT
    ) ;

INSERT INTO @Parent
VALUES
    ( 1, 'One' )
  , ( 2, 'Two' )
  , ( 3, 'Three' )
  , ( 5, 'Four' ) ;

INSERT INTO @Children
VALUES
    ( 10, 1 )
  , ( 11, 1 )
  , ( 21, 2 )
  , ( 23, 2 )
  , ( 30, 3 )
  , ( 40, 4 ) ;

目标: 为了高效输出三个字段( [Id] 和 [IsChild]、[Misc_Val] )。使用 [IsChild] = 0 输出 @Parent 表中的所有记录,并使用 [IsChild] = 1 输出 @Child 表 (@Parent.Id = @Children.P_Id) 中的所有 MATCHING 记录。

预期输出

Id  IsChild Misc_Val
1   0       One
2   0       Two
3   0       Three
5   0       Four
10  1       One
11  1       One
21  1       Two
23  1       Two
30  1       Three

我的尝试:

SELECT  [P].[Id]
      , 0 AS [IsChild]
      , [P].[Misc_Val]
FROM    @Parent AS [P]
UNION ALL
SELECT  [C].[Id]
      , 1
      , [P].[Misc_Val]
FROM    @Parent AS [P]
JOIN    @Children AS [C]
ON      [C].[P_ID] = [P].[Id] ;

还有比使用 UNION ALL 更好的方法吗? @Parent 和 @Children 表非常大,因此我试图避免两次查询 @Parent 表。

更新:下面的答案让我意识到在使用模拟数据创建帖子时我错过了一些东西。不管在最终输出中,我们确实需要来自@Parent 表的一些额外数据。

【问题讨论】:

  • 您有性能问题吗?因为如果不是...不要尝试预先优化它。 UNION ALL 是解决此问题的正确方法,因为您将数据存储在 2 个表中(通常您会使用同一个表,引用自身)。
  • 不幸的是,由于两个表中的行数都非常多,再次查询@Parent 表对系统来说是一个很大的问题。
  • 这是一个写得很好的问题,但是对于性能问题,您应该始终使用Paste The Plan 发布实际查询(而不是您的模拟查询)的执行计划。您也不应该假设您知道提高性能的最佳方法(在这种情况下删除union all),因为这可能不是解决方案。你的问题应该是,我怎样才能提高这个查询的性能。
  • 对我来说有点像左外连接。如果有帮助,UNION 会消除重复项,而 UNION ALL 则不会
  • @DaleK 我已经多次遇到过这种情况,这让我很恼火,SQL 中没有简单的方法来表达它。 CROSS APPLY 在我的回答中并不总是有效的,特别是如果子表没有很好的索引。我希望有一个OUTER WITH INNER JOIN

标签: sql sql-server tsql optimization sql-server-2016


【解决方案1】:

您可以使用CROSS APPLY将子表添加到父表中。

这可能会或可能不会更快,它可能取决于索引等。您需要检查查询计划。

SELECT  v.Id
      , v.IsChild
      , P.Misc_Val
FROM    @Parent AS P
CROSS APPLY (
    SELECT
        P.Id,
        0 AS IsChild
    UNION ALL
    SELECT
        C.Id,
        1
      FROM @Children AS C
      WHERE C.P_ID = P.Id
) v;

请注意,apply 中的第一个SELECT 没有FROM,因此不会进行任何表访问。

【讨论】:

  • 感谢查理菲斯的建议。我测试了这种方法(顺便说一句很有趣)。 @parent 表的扫描和逻辑读取减少了一半,但是,扫描、逻辑读取和物理读取增加了很多。它在 PK 上进行索引搜索,其中搜索谓词 = PK 中的第一个字段(有两个字段,在选择中返回第二个字段)。
  • 是的,它可以做到这一点,它通常会强制嵌套循环,这只有在子表具有良好的索引时才有用
  • 明白了。它在 PK 上搜索,其中搜索谓词是第一个键,而 SELECT 从该 PK 返回第二个键。您是否推荐我可以做的任何索引改进以获得更好的查询执行计划?顺便说一句,“......扫描、逻辑读取和物理读取跳跃了很多......”是针对@child 表的(无法编辑我原来的评论)。
  • 您必须通过brentozar.com/pastetheplan分享查询计划
  • 好的,如果我能做到,我将不得不与高于我薪级的人核实。如果允许,将发布。谢谢
猜你喜欢
  • 1970-01-01
  • 2014-10-07
  • 1970-01-01
  • 1970-01-01
  • 2023-02-20
  • 2021-11-14
  • 1970-01-01
  • 2011-04-07
  • 1970-01-01
相关资源
最近更新 更多