SQL UNION ALL消除重复

时间:2017-01-18 20:43:05

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

我发现这个样本面试的问题和答案在这里转载。但我真的不懂代码。 UNION ALL如何变成UNIION(不同)?另外,为什么这段代码更快?

问题

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

ANSWER

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.

6 个答案:

答案 0 :(得分:8)

但是在示例中,第一个查询在列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操作。

答案 1 :(得分:0)

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

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

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

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

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

答案 2 :(得分:0)

我想它会起作用

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

答案 3 :(得分:0)

这也会起到相同的作用:

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)

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

我认为这是对的(甲骨文):

select distinct * from (

select * from test_a

union all

select * from test_b
);