Union的语义是把两部分查询的结果合并起来,最终结果的列名和类型定义与第一个查询一致。Union语句可以是Union All或者Union Distinct,默认情况下最好采用前者,即只有Union关键字时等价于Union All。下面看看Union All/Union Distinct的例子。
表A 表B
1 | 2 |
3 | 4 |
3 | 4 |
3 | 4 |
5 | 6 |
3 | 4 |
3 | 4 |
7 | 8 |
UnionAll
1 | 2 |
3 | 4 |
3 | 4 |
3 | 4 |
5 | 6 |
3 | 4 |
3 | 4 |
7 | 8 |
UnionDistinct
1 | 2 |
3 | 4 |
5 | 6 |
7 | 8 |
MergeUnion
ALL
如果是Union All,那么MergeUnion的两个输入表没有必要是有序的,MergeUnion只需要先输出第一个表的数据,再输出第二个表的数据就可以了。
Distinct
如果是Union Distinct,MergeUnion算法要求两个输入表数据都有相同的排序。假设两个输入表的行数分别为M、N,则MergeUnion算法复杂度为O(M+N)。
具体实现如下:
1. 如果第一个表的当前元组小于第二个表的当前元组,或者第二个表结束,那么输出第一个表的元组,且跳过相等的元组。
2. 如果第一个表的当前元组大于第二个表的当前元组,或者第一个表结束,那么输出第二个表的元组,且跳过相等的元组。
3. 如果第一个表的当前元组等于第二个表的当前元组,那么输出第一个表的元组,且跳过第一个和第二个表的相同元组。
4 如果两个表都结束,则返回结束。
其余思想类似。前提都是需要排序。不排序,则可以采用Hash的思路。思路见SQL Distinct的实现
-----
附SQL集合运算 差集 并集 交集的入门介绍:
SQL集合运算 差集 并集 交集
SQL-3标准中提供了三种对检索结果进行集合运算的命令:并集UNION;交集INTERSECT;差集EXCEPT(在Oracle中叫做 MINUS)。在有些数据库中对此的支持不够充分,如MySql中只有UNION,没有其他两种。实际上这些运算都可以通过普通的SQL来实现,虽然有时有些繁琐。
假设有两个表(或视图)s,t,s中有两个字段sa,sb;t中有两个字段ta,tb;
差集EXCEPT:
PLAIN TEXT
SQL:
- SELECTsaFROMs
- EXCEPT
- SELECTtaFROMt;
可以写作
PLAIN TEXT
SQL:
- SELECTsaFROMs
- WHEREsaNOTIN
- (SELECTtaFROMt)
上面的例子中忽略了对s和t单独的条件,这些总可以加入AND条件完成,或者使用视图。如果是多个字段比较麻烦,如:
PLAIN TEXT
SQL:
- SELECTsa, sbFROMs
- EXCEPT
- SELECTta, tbFROMt;
需要写成
PLAIN TEXT
SQL:
- SELECTsa, sbFROMs
- WHERE(sa, sb)NOTIN
- (SELECTta, tbFROMt)
上面使用的语法不见得数据库都支持。好在不支持EXCEPT的MySQL支持这种语法,而不支持这种语法的MSSQL又支持EXCEPT。
注意对于这样的row constructors(Mysql术语),是和下面写法(以及其他类似写法)不等价的。
PLAIN TEXT
SQL:
- SELECTsa, sbFROMs
- WHEREsaNOTIN
- (SELECTtaFROMt)
- ANDsbNOTIN
- (SELECTtbFROMt)
在MSSQL中的一个解决技巧是,把这两个字段(假设字符类型)拼起来,即
PLAIN TEXT
SQL:
- SELECTsa, sbFROMs
- WHEREsa+sbNOTIN
- (SELECTta+tbFROMt)
交集INTERSECT:
PLAIN TEXT
SQL:
- SELECTsaFROMs
- INTERSECT
- SELECTtaFROMt;
可以写成
PLAIN TEXT
SQL:
- SELECTsaFROMs
- WHEREsa IN
- (SELECTtaFROMt)
当然也可以写成
PLAIN TEXT
SQL:
- SELECTsaFROMs
- WHEREEXISTS
- (SELECT*FROMtWHEREt.ta=s.sa)
或者使用连接
PLAIN TEXT
SQL:
- SELECTsaFROMs, t
- WHEREsa = ta
实际上这几个语句都有点问题,就是INTERSECT在出现重复时的语义问题。按照SQL-3标准,类似UNION,可以有明确的 INTERSECT ALL或者INTERSECT DISTINCT语法。一般的INTERSECT实现并没有明确这一点,而且从逻辑上讲意义也不大。那么当s或t中出现重复的时,如sa='x'的有2 个,sb='x'的有3个,使用上面的子查询将返回2行,使用连接将返回6行,当然这两个语句都可以加上一个DISTINCT,就实现了 INTERSECT DISTINCT语义了。
并集UNION:
MySql从4.0开始就支持UNION(ALL 和 DISTINCT)了,为完整起见,也列举一下。
其实实现这样一个结果是很麻烦的
PLAIN TEXT
SQL:
- SELECTsaFROMs
- DISTINCT
- SELECTtaFROMt;
需要使用外连接,而且是Full的外连接
PLAIN TEXT
SQL:
- SELECTDISTINCTNVL(s.sa, t.ta)
- FROMs FULLOUTERJOINtON(s.sa=t.ta)
上面的例子中我使用了Oracle的语法,实际上MySql不支持FULL OUTER JOIN(虽然支持LEFT和RIGHT OUTER JOIN),好在MySql支持UNION。
对于UNION ALL语义,我还没有想出来用普通查询如何实现,如果在上面语句中去掉DISTINCT,结果肯定不对。
标签:Union,TEXT,SQL,元组,INTERSECT,PLAIN,集合 From: https://blog.51cto.com/maray/6921040