GROUP BY 优化,包含 OR 条件的分组规则

Posted

技术标签:

【中文标题】GROUP BY 优化,包含 OR 条件的分组规则【英文标题】:Optimization of GROUP BY which contains the grouping rules with OR condition 【发布时间】:2019-04-22 07:27:39 【问题描述】:

有一些表T1(在Oracle数据库中)有一些字段ABCDEF

更新0:让上述字段的类型相同。

假设,我们需要按照以下规则对表格进行分组: A & B & (C | D)

更新 1: A & B & (C | D) 表达式可以转换为以下表达式:

(A & B & C) | (A & B & D)

因此,为了解决这个任务,我必须为组@98​​7654332@ 和A, B, D 合并两个分组查询:

select A, B, C, count(*) 
from T1 
group by A, B, C

  union all

select A, B, D, count(*) 
from T1 
group by A, B, D

如果分组规则会更复杂:A & B & (C | D) & (E | F),那么解决方案会更笨重,因为我必须对以下组进行联合分组查询:

A & B & C & EA & B & D & EA & B & C & FA & B & D & F

有没有可能优化这样的解决方案? 或者可能有更好的方法来解决这些任务?

更新 2: 我使用了简短的表达式A & B & (C | D)A & B & (C | D) & (E | F) 来强调它们有一个共同的部分A & B。而且我不希望它被计算多次。

【问题讨论】:

更新您的问题添加适当的数据样本和预期结果 您可以查看group by grouping sets 以及group by 的相关cuberollup 等扩展。样本数据和结果会有所帮助。 这是什么意思:“假设,我们需要按照以下规则对表格进行分组:A & B & (C | D)”? @Gordon Linoff,请在主要问题中查看更新Upd1Upd2 在您的查询结果中,您看不到您实际显示的是 C 还是 D。然后,UNION(相对于UNION ALL)甚至会删除重复项,因此如果您有 A=1,B=2,C=3,COUNT=4 和 A=1,B=2,CD=3,COUNT =4 在你的结果中只有一次。不是 COUNT=4 的两行,不是 COUNT=8 的一行,而是 COUNT=4 的一行。这一切都是想要的吗?似乎不太可能。而且它看起来不仅是查询,甚至您的数据模型也可能不合适。 【参考方案1】:

GROUPING SETS子句可以简化代码,提高多个分组组合的性能。

更简单的代码

举个例子,让我们从一个简单的表格开始:

create table t1(a number, b number, c number, d number);
insert into t1
select 0,0,0,0 from dual union all
select 1,0,0,0 from dual union all
select 0,1,0,0 from dual union all
select 1,1,0,0 from dual union all
select 0,0,1,0 from dual union all
select 1,0,1,0 from dual union all
select 0,1,1,0 from dual union all
select 1,1,1,0 from dual union all
select 0,0,0,1 from dual union all
select 1,0,0,1 from dual union all
select 0,1,0,1 from dual union all
select 1,1,0,1 from dual union all
select 0,0,1,1 from dual union all
select 1,0,1,1 from dual union all
select 0,1,1,1 from dual union all
select 1,1,1,1 from dual;

以下查询表示按“A & (B | C)”进行分组。 (与您的示例不同,我将包含一些空列来演示分组的工作原理。)

select a, b, null c, count(*)
from t1
group by a, b
union all
select a, null b, c, count(*)
from t1
group by a, c;

A   B   C   COUNT(*)
-   -   -   --------
1   0              4
0   0              4
1   1              4
0   1              4
1      0           4
0      0           4
1      1           4
0      1           4

使用GROUPING SETS 重写会产生与前面的查询相同的结果:

select a, b, c, count(*)
from t1
group by grouping sets((a, b), (a, c));

更好的性能

使用explain plan for ... 运行上述查询,然后使用select * from table(dbms_xplan.display(format => 'basic')); 返回以下执行计划。

对于UNION ALL 版本:

------------------------------------
| Id  | Operation           | Name |
------------------------------------
|   0 | SELECT STATEMENT    |      |
|   1 |  UNION-ALL          |      |
|   2 |   HASH GROUP BY     |      |
|   3 |    TABLE ACCESS FULL| T1   |
|   4 |   HASH GROUP BY     |      |
|   5 |    TABLE ACCESS FULL| T1   |
------------------------------------

对于GROUPING SETS 版本:

-------------------------------------------------------------------------------
| Id  | Operation                                | Name                       |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                         |                            |
|   1 |  TEMP TABLE TRANSFORMATION               |                            |
|   2 |   LOAD AS SELECT (CURSOR DURATION MEMORY)| SYS_TEMP_0FD9D6787_464CF95 |
|   3 |    TABLE ACCESS FULL                     | T1                         |
|   4 |   LOAD AS SELECT (CURSOR DURATION MEMORY)| SYS_TEMP_0FD9D6788_464CF95 |
|   5 |    HASH GROUP BY                         |                            |
|   6 |     TABLE ACCESS FULL                    | SYS_TEMP_0FD9D6787_464CF95 |
|   7 |   LOAD AS SELECT (CURSOR DURATION MEMORY)| SYS_TEMP_0FD9D6788_464CF95 |
|   8 |    HASH GROUP BY                         |                            |
|   9 |     TABLE ACCESS FULL                    | SYS_TEMP_0FD9D6787_464CF95 |
|  10 |   VIEW                                   |                            |
|  11 |    TABLE ACCESS FULL                     | SYS_TEMP_0FD9D6788_464CF95 |
-------------------------------------------------------------------------------

UNION ALL 执行计划为每个不同的分组从源表中读取一次。 GROUPING SETS 执行计划只从源表读取一次,将信息存储在临时表中,然后从该临时表中读取。

如果查询只使用行的一小部分子集或列的小子集,GROUPING SETS 计划可能会明显更快,因为它只需读取一次完整数据。

【讨论】:

感谢您的精彩解释!

以上是关于GROUP BY 优化,包含 OR 条件的分组规则的主要内容,如果未能解决你的问题,请参考以下文章

在SQL中分组查询 Group by 的存在条件是啥

SQL group by分组查询

MySql - GROUP BY 和 HAVING关键字

mysql中group by里面的问题

关于C#中group by如何实现多条件分组汇总

MySQL数据库:group分组