数据库中的集合操作可以把多个查询的结果组合成一个结果集。

集合操作主要包含:

  • UNION

  • INTERSECT

  • EXCEPT/MINUS

注意

OceanBase 数据库同时支持 EXCEPT 与 MINUS,这两者的语义和语法都是相同的。

对于 UNION 来说用户可以指定 UNION 的属性为 ALL(集合可重复) 和 DISTINCT/UNIQUE(集合不可重复)。所有的集合操作默认的属性是 DISTINCT(OceanBase 数据库的 DISTINCT 和 UNIQUE 的语义语法是相同的),而其他的几种集合操作是不能指定属性的。

执行计划

所有的集合操作在逻辑计划层面的展示都是 [UNION|INTERSECT|EXCEPT] [ALL|DISTINCT] 的形式。带有 DISTINCT 关键字时,需要从输出的结果中去掉重复的行,目前 OceanBase 数据库只支持基于排序的集合 DISTINCT 去重,在计划层面,排序操作会下压到各分支内部进行。

  1. obclient> explain select * from t1 union select * from t1\G
  2. *************************** 1. row ***************************
  3. Query Plan:
  4. =======================================
  5. |ID|OPERATOR |NAME|EST. ROWS|COST|
  6. ---------------------------------------
  7. |0 |UNION DISTINCT| |2000 |4861|
  8. |1 | SORT | |1000 |2048|
  9. |2 | TABLE SCAN |t1 |1000 |498 |
  10. |3 | SORT | |1000 |2048|
  11. |4 | TABLE SCAN |t1 |1000 |498 |
  12. =======================================
  13. Outputs & filters:
  14. -------------------------------------
  15. 0 - output([UNION(t1.c1, t1.c1)], [UNION(t1.c2, t1.c2)]), filter(nil)
  16. 1 - output([t1.c1], [t1.c2]), filter(nil), sort_keys([t1.c1, ASC], [t1.c2, ASC])
  17. 2 - output([t1.c1], [t1.c2]), filter(nil),
  18. access([t1.c1], [t1.c2]), partitions(p0)
  19. 3 - output([t1.c1], [t1.c2]), filter(nil), sort_keys([t1.c1, ASC], [t1.c2, ASC])
  20. 4 - output([t1.c1], [t1.c2]), filter(nil),
  21. access([t1.c1], [t1.c2]), partitions(p0)
  22. 1 row in set (0.02 sec)
  23. obclient> explain select * from t1 intersect select * from t1\G
  24. *************************** 1. row ***************************
  25. Query Plan:
  26. ===========================================
  27. |ID|OPERATOR |NAME|EST. ROWS|COST|
  28. -------------------------------------------
  29. |0 |INTERSECT DISTINCT| |1000 |4723|
  30. |1 | SORT | |1000 |2048|
  31. |2 | TABLE SCAN |t1 |1000 |498 |
  32. |3 | SORT | |1000 |2048|
  33. |4 | TABLE SCAN |t1 |1000 |498 |
  34. ===========================================
  35. Outputs & filters:
  36. -------------------------------------
  37. 0 - output([INTERSECT(t1.c1, t1.c1)], [INTERSECT(t1.c2, t1.c2)]), filter(nil)
  38. 1 - output([t1.c1], [t1.c2]), filter(nil), sort_keys([t1.c1, ASC], [t1.c2, ASC])
  39. 2 - output([t1.c1], [t1.c2]), filter(nil),
  40. access([t1.c1], [t1.c2]), partitions(p0)
  41. 3 - output([t1.c1], [t1.c2]), filter(nil), sort_keys([t1.c1, ASC], [t1.c2, ASC])
  42. 4 - output([t1.c1], [t1.c2]), filter(nil),
  43. access([t1.c1], [t1.c2]), partitions(p0)
  44. 1 row in set (0.02 sec)
  45. obclient> explain select * from t1 minus select * from t1\G
  46. *************************** 1. row ***************************
  47. Query Plan:
  48. ========================================
  49. |ID|OPERATOR |NAME|EST. ROWS|COST|
  50. ----------------------------------------
  51. |0 |EXCEPT DISTINCT| |1000 |4723|
  52. |1 | SORT | |1000 |2048|
  53. |2 | TABLE SCAN |t1 |1000 |498 |
  54. |3 | SORT | |1000 |2048|
  55. |4 | TABLE SCAN |t1 |1000 |498 |
  56. ========================================
  57. Outputs & filters:
  58. -------------------------------------
  59. 0 - output([EXCEPT(t1.c1, t1.c1)], [EXCEPT(t1.c2, t1.c2)]), filter(nil)
  60. 1 - output([t1.c1], [t1.c2]), filter(nil), sort_keys([t1.c1, ASC], [t1.c2, ASC])
  61. 2 - output([t1.c1], [t1.c2]), filter(nil),
  62. access([t1.c1], [t1.c2]), partitions(p0)
  63. 3 - output([t1.c1], [t1.c2]), filter(nil), sort_keys([t1.c1, ASC], [t1.c2, ASC])
  64. 4 - output([t1.c1], [t1.c2]), filter(nil),
  65. access([t1.c1], [t1.c2]), partitions(p0)
  66. 1 row in set (0.01 sec)

如下例所示,带有 ALL 属性的集合操作不要求下层有序。

  1. obclient> explain select * from t1 union all select * from t1\G
  2. *************************** 1. row ***************************
  3. Query Plan:
  4. ====================================
  5. |ID|OPERATOR |NAME|EST. ROWS|COST|
  6. ------------------------------------
  7. |0 |UNION ALL | |2000 |1761|
  8. |1 | TABLE SCAN|t1 |1000 |498 |
  9. |2 | TABLE SCAN|t1 |1000 |498 |
  10. ====================================
  11. Outputs & filters:
  12. -------------------------------------
  13. 0 - output([UNION(t1.c1, t1.c1)], [UNION(t1.c2, t1.c2)]), filter(nil)
  14. 1 - output([t1.c1], [t1.c2]), filter(nil),
  15. access([t1.c1], [t1.c2]), partitions(p0)
  16. 2 - output([t1.c1], [t1.c2]), filter(nil),
  17. access([t1.c1], [t1.c2]), partitions(p0)
  18. 1 row in set (0.02 sec)

当集合操作两边出现类型不一致的时候,SQL 层会为 UNION 左右两边的表达式分配 CAST 转换,这个操作在 EXPLAIN 中是可见的。

  1. obclient> create table t1 (c1 int, c2 varchar(5));
  2. Query OK, 0 rows affected (0.12 sec)
  3. obclient> explain select c1 from t1 union select c2 from t1\G
  4. *************************** 1. row ***************************
  5. Query Plan:
  6. ========================================
  7. |ID|OPERATOR |NAME|EST. ROWS|COST |
  8. ----------------------------------------
  9. |0 |UNION DISTINCT| |2000 |11986|
  10. |1 | SORT | |1000 |5664 |
  11. |2 | TABLE SCAN |t1 |1000 |476 |
  12. |3 | SORT | |1000 |5664 |
  13. |4 | TABLE SCAN |t1 |1000 |476 |
  14. ========================================
  15. Outputs & filters:
  16. -------------------------------------
  17. 0 - output([UNION(cast(t1.c1, VARCHAR(11)), cast(t1.c2, VARCHAR(11)))]), filter(nil)
  18. 1 - output([cast(t1.c1, VARCHAR(11))]), filter(nil), sort_keys([cast(t1.c1, VARCHAR(11)), ASC])
  19. 2 - output([cast(t1.c1, VARCHAR(11))]), filter(nil),
  20. access([t1.c1]), partitions(p0)
  21. 3 - output([cast(t1.c2, VARCHAR(11))]), filter(nil), sort_keys([cast(t1.c2, VARCHAR(11)), ASC])
  22. 4 - output([cast(t1.c2, VARCHAR(11))]), filter(nil),
  23. access([t1.c2]), partitions(p0)
  24. 1 row in set (0.01 sec)