【问题标题】:SQL UNION ALL to eliminate duplicatesSQL UNION ALL 消除重复
【发布时间】:2023-03-06 08:28:01
【问题描述】:

我在此处复制了 toptal 上发布的这个示例面试问题和答案。但我真的不明白代码。一个 UNION ALL 怎么能变成这样的一个 UNION (distinct)?另外,为什么这段代码更快?

问题

使用 UNION ALL(不是 UNION)编写 SQL 查询,该查询使用 WHERE 子句消除重复项。你为什么要这样做? 隐藏答案 您可以通过运行如下查询来避免使用 UNION ALL 重复,并且运行速度仍然比 UNION DISTINCT(实际上与 UNION 相同)快得多:

回答

SELECT * FROM mytable WHERE a=X UNION ALL SELECT * FROM mytable WHERE b=Y AND a!=X

The key is the AND a!=X part. This gives you the benefits of the UNION (a.k.a., UNION DISTINCT) command, while avoiding much of its performance hit.

【问题讨论】:

  • 这种解释具有误导性......它本质上是在做一个UNION ALL,但过滤掉了第一个查询的结果。它只是使用WHERE 子句进行过滤,而不是使用昂贵的DISTINCT 操作。
  • 答案是错误的。此查询不会消除表中已经存在的重复项,而 UNION DISTICT 可以。
  • @PaulSpiegel,假设有一个主键不涉及a,由于select *,就不会有重复。
  • 能否引用问答的出处?你确定这是问答的全部内容吗?

标签: mysql sql sql-server union union-all


【解决方案1】:

如果表具有唯一标识符 - 主键,则问题将是正确的。否则每个选择都可以返回许多相同的行。

要理解为什么它可以更快,让我们看看数据库如何执行 UNION ALL 和 UNION。

第一个是来自两个独立查询的简单连接结果。这些查询可以并行处理并一一提交给客户端。

二是加入+区分。要从 2 个查询中区分记录,db 需要将它们全部保存在内存中,或者如果内存不足,db 需要将它们存储到临时表中,然后选择唯一的。这就是性能下降的地方。 DB 非常聪明,并且区分算法开发得很好,但对于大型结果集,无论如何这可能是个问题。

如果在过滤时使用索引,则 UNION ALL + 附加 WHERE 条件会更快。 所以,这就是性能的魔力。

【讨论】:

    【解决方案2】:

    但在示例中,第一个查询在 a 列上具有条件,而第二个查询在 b 列上具有条件。这可能来自一个难以优化的查询:

    SELECT * FROM mytable WHERE a=X OR b=Y
    

    这个查询很难用简单的 B 树索引来优化。引擎是否在列a 上搜索索引?还是在b 列上?无论哪种方式,搜索另一个术语都需要表扫描。

    因此,使用 UNION 将每个查询分成两个查询的技巧。每个子查询可以为每个搜索词使用最佳索引。然后使用 UNION 组合结果。

    但是这两个子集可能会重叠,因为b=Y 的某些行也可能有a=X,在这种情况下,这样的行会出现在两个子集中。因此,您必须进行重复消除,否则在最终结果中会看到某些行两次。

    SELECT * FROM mytable WHERE a=X 
    UNION DISTINCT
    SELECT * FROM mytable WHERE b=Y
    

    UNION DISTINCT 很昂贵,因为典型的实现对行进行排序以查找重复项。就像你使用SELECT DISTINCT ...一样。

    我们还认为,如果您合并的两个行子集在两个子集中都出现了很多行,则会更加“浪费”工作。要消除的行数很多。

    但是,如果您可以保证两组行已经不同,则无需消除重复项。也就是说,如果你保证没有重叠。如果您可以依赖它,那么消除重复项始终是无操作的,因此查询可以跳过该步骤,从而跳过代价高昂的排序。

    如果您更改查询以保证它们选择不重叠的行子集,那就成功了。

    SELECT * FROM mytable WHERE a=X 
    UNION ALL 
    SELECT * FROM mytable WHERE b=Y AND a!=X
    

    这两组保证没有重叠。如果第一组有a=X 的行,而第二组有a!=X 的行,那么这两个集合中都没有行。

    因此,第二个查询仅捕获 一些 b=Y 所在的行,但 a=X AND b=Y 已包含在第一组中的任何行。

    因此该查询实现了对两个OR 术语的优化搜索,不会产生重复项,也不需要UNION DISTINCT 操作。

    【讨论】:

    • 哇,这个答案对我来说很有意义。但是为什么有些人说它是错误的?在某些情况下会失败吗?
    • 反对意见是它没有考虑表本身具有重复行的情况(这在规范化数据库中绝不应该是这种情况,但是嘿它确实发生了)。带有UNION DISTINCT 的查询将消除结果集中的重复项。 UNION ALL 保留此类重复项。
    【解决方案3】:

    我想它会起作用的

    select col1 From (
    select row_number() over (partition by col1 order by col1) as b, col1 
    from (
    select col1  From u1
    union all
    select col1 From u2 ) a
    ) x
    where x.b =1
    

    【讨论】:

      【解决方案4】:

      这也会起到同样的作用:

      select * from (
      select * from table1
      union all 
      select * from table2
      ) a group by 
      columns
      having count(*) >= 1
      

      select * from table1 
      union all
      select * from table2 b 
      where not exists (select 1 from table1 a where a.col1 = b.col1)
      

      【讨论】:

        【解决方案5】:

        最简单的方法是这样的,特别是如果你有很多列:

        SELECT *
          INTO table2
          FROM table1
          UNION
        SELECT *
          FROM table1
          ORDER BY column1
        

        【讨论】:

        • 您好,感谢您的回答。这会创建table2,而不是问题作者所问的。尝试从帖子中回答“UNION ALL 如何变成这样的 UNION(不同)?另外,为什么这段代码更快?”。
        【解决方案6】:

        我来宾这是对的(甲骨文):

        select distinct * from (
        
        select * from test_a
        
        union all
        
        select * from test_b
        );      
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-09-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-03-03
          • 1970-01-01
          相关资源
          最近更新 更多