SQL UNION ALL 消除重复项

SQL UNION ALL to eliminate duplicates

我发现这个样本面试问题和答案张贴在 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.

如果 table 具有唯一标识符 - 主键,则该问题将是正确的。否则每个 select 可以 return 许多相同的行。

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

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

二是加入+区分。要区分来自 2 个查询的记录,db 需要将它们全部存储在内存中,或者如果内存不足,db 需要将它们存储到临时 table 和下一个 select 唯一的。这就是性能下降的地方。数据库非常聪明,区分算法也开发得很好,但对于大型结果集来说,这无论如何都可能是个问题。

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

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

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

此查询很难用简单的 B-tree 索引优化。引擎是否搜索列 a 上的索引?还是在 b 列?无论哪种方式,搜索其他术语都需要 table-scan。

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

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

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

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

我们还认为,如果要合并的行的两个子集在两个子集中都出现很多行,那么 "wasted" 会更有效。要删除的行很多。

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

如果您更改查询以保证它们是 select non-overlapping 行的子集,那就成功了。

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 操作。

我想它会起作用

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

这也会做同样的事情:

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)

最简单的方法是这样的,尤其是当你有很多列时:

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

我猜这是对的(Oracle):

select distinct * from (

select * from test_a

union all

select * from test_b
);