现在的位置: 首页 > 综合 > 正文

SQL集合操作Union实现

2013年12月05日 ⁄ 综合 ⁄ 共 2413字 ⁄ 字号 评论关闭

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集合运算 差集 并集 交集的入门介绍:

附文来源:http://www.cnblogs.com/shengtianlong/archive/2010/12/03/1895346.html

SQL集合运算 差集 并集 交集

SQL-3标准中提供了三种对检索结果进行集合运算的命令:并集UNION;交集INTERSECT;差集EXCEPT(在Oracle中叫做 MINUS)。在有些数据库中对此的支持不够充分,如MySql中只有UNION,没有其他两种。实际上这些运算都可以通过普通的SQL来实现,虽然有时有些繁琐。

假设有两个表(或视图)s,t,s中有两个字段sa,sb;t中有两个字段ta,tb;

差集EXCEPT:

PLAIN TEXT
SQL:

  1. SELECTsaFROMs
  2. EXCEPT
  3. SELECTtaFROMt;

可以写作

PLAIN TEXT
SQL:

  1. SELECTsaFROMs
  2. WHEREsaNOTIN
  3.    (SELECTtaFROMt)

上面的例子中忽略了对s和t单独的条件,这些总可以加入AND条件完成,或者使用视图。如果是多个字段比较麻烦,如:

PLAIN TEXT
SQL:

  1. SELECTsa, sbFROMs
  2. EXCEPT
  3. SELECTta, tbFROMt;

需要写成

PLAIN TEXT
SQL:

  1. SELECTsa, sbFROMs
  2. WHERE(sa, sb)NOTIN
  3.    (SELECTta, tbFROMt)

 

上面使用的语法不见得数据库都支持。好在不支持EXCEPT的MySQL支持这种语法,而不支持这种语法的MSSQL又支持EXCEPT。

注意对于这样的row constructors(Mysql术语),是和下面写法(以及其他类似写法)不等价的。

PLAIN TEXT
SQL:

  1. SELECTsa, sbFROMs
  2. WHEREsaNOTIN
  3.    (SELECTtaFROMt)
  4. ANDsbNOTIN
  5.    (SELECTtbFROMt)

在MSSQL中的一个解决技巧是,把这两个字段(假设字符类型)拼起来,即

PLAIN TEXT
SQL:

  1. SELECTsa, sbFROMs
  2. WHEREsa+sbNOTIN
  3.    (SELECTta+tbFROMt)

 

交集INTERSECT:

PLAIN TEXT
SQL:

  1. SELECTsaFROMs
  2. INTERSECT
  3. SELECTtaFROMt;

可以写成

PLAIN TEXT
SQL:

  1. SELECTsaFROMs
  2. WHEREsa IN
  3.    (SELECTtaFROMt)

当然也可以写成

PLAIN TEXT
SQL:

  1. SELECTsaFROMs
  2. WHEREEXISTS
  3.    (SELECT*FROMtWHEREt.ta=s.sa)

或者使用连接

PLAIN TEXT
SQL:

  1. SELECTsaFROMs, t
  2. 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:

  1. SELECTsaFROMs
  2. UNIONDISTINCT
  3. SELECTtaFROMt;

需要使用外连接,而且是Full的外连接

PLAIN TEXT
SQL:

  1. SELECTDISTINCTNVL(s.sa, t.ta)
  2. FROMs FULLOUTERJOINtON(s.sa=t.ta)

 

上面的例子中我使用了Oracle的语法,实际上MySql不支持FULL OUTER JOIN(虽然支持LEFT和RIGHT OUTER JOIN),好在MySql支持UNION。

对于UNION ALL语义,我还没有想出来用普通查询如何实现,如果在上面语句中去掉DISTINCT,结果肯定不对。

标签: 集合差集

【上篇】
【下篇】

抱歉!评论已关闭.