我遇到过使用1 = 1条件聚合而不是计数
的编码select sum(case when 1=1 then 1 else 0 end)
在我看来,这相当于:
select count(1)
起初我认为这是一个占位符,开发人员打算在这个条件聚合中重新访问一些额外的逻辑,但后来我在另一个脚本中找到它,然后是另一个脚本。这促使我四处询问,我发现之前的开发人员已经强调,这个总和条件在所有情况下都比计数函数更有效和更快(强调它足以让其他开发人员遵循以后的标准)。对我来说似乎非常直观,迫使系统评估1 = 1条件可能很小,但它仍然是计数函数的额外工作。在我回来之前,我会在这里咨询一下。
a)这个开发人员说条件聚合将比计数更快是否有任何道理
完全侧面的问题: b)有没有一个数据库系统可以比计数更快地评估条件聚合?
这是一个oracle 11g数据库,但我怀疑脚本是为oracle 8i编写的
作为奖励积分......我被要求优化此代码。将删除替换为计数函数会改善速度吗?记录数有时超过1亿。
答案 0 :(得分:3)
执行摘要:它没有任何区别,而且它从来没有在Oracle中,因为至少版本6(1989),当我第一次开始听到通过选择主键列等快速计算的聪明方法时,就好像Oracle是不知道人们有时会数事。
通过在过滤器中使用它并检查执行计划的“Predicates”部分,您可以看到解析器/优化器对表达式的作用。
create table demo
( demo_id integer generated always as identity constraint demo_pk primary key
, othercolumn integer );
insert into demo (othercolumn) select dbms_random.value(0,1000)
from dual connect by rownum <= 10000;
commit;
call dbms_stats.gather_table_stats(user, 'demo');
正常count(*)
(Oracle 12.1):
select count(*) from demo
having count(*) > 1
Plan hash value: 1044424301
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 7 (0)| 00:00:01 |
|* 1 | FILTER | | | | |
| 2 | SORT AGGREGATE | | 1 | | |
| 3 | INDEX FAST FULL SCAN| DEMO_PK | 10000 | 7 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(COUNT(*)>1)
聪明的超高速表达:
select sum(case when 1=1 then 1 else 0 end) from demo
having sum(case when 1=1 then 1 else 0 end) > 0
Plan hash value: 1044424301
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 7 (0)| 00:00:01 |
|* 1 | FILTER | | | | |
| 2 | SORT AGGREGATE | | 1 | | |
| 3 | INDEX FAST FULL SCAN| DEMO_PK | 10000 | 7 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(SUM(1)>0)
注意Predicates部分,该部分显示已评估sum
表达式并替换为sum(1)
。 (我现在没有时间深入研究tracefiles,但我很确定他们会证明重写是在CBO优化之前发生的。)
以下是它对count(1)
的作用,另一种表达式有时被认为比标准表达式更有效:
select count(1) from demo
having count(1) > 1
Plan hash value: 1044424301
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 7 (0)| 00:00:01 |
|* 1 | FILTER | | | | |
| 2 | SORT AGGREGATE | | 1 | | |
| 3 | INDEX FAST FULL SCAN| DEMO_PK | 10000 | 7 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(COUNT(*)>1)
这是没有过滤器的计划:
select sum(case when 1=1 then 1 else 0 end) as rowcount
from demo
Plan hash value: 2242940774
-------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 7 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FAST FULL SCAN| DEMO_PK | 10000 | 7 (0)| 00:00:01 |
-------------------------------------------------------------------------
正如您所看到的,它们都是相同的(除了我的人工过滤条件的差异)。
此外,如果没有行,sum(1)
与count(*)
的结果不同:
select sum(case when 1=1 then 1 else 0 end) as sum1
, count(*)
from demo
where 1=2
SUM1 COUNT(*)
---------- ----------
0
答案 1 :(得分:1)
找到答案的最简单方法是,我猜,解释这两个选项,看看Oracle说的是什么。
首先,通常的 COUNT选项:
SQL> set autotrace on explain
SQL> select /*+ choose */ count(*) from tob_stavke_rac;
COUNT(*)
----------
53195373
Execution Plan
----------------------------------------------------------
Plan hash value: 3099656827
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30846 (2)| 00:00:02 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FAST FULL SCAN| SRC_S_STA_FK_I | 53M| 30846 (2)| 00:00:02 |
--------------------------------------------------------------------------------
然后,异常 SUM与CASE:
SQL> select /*+ choose */ sum(case when 1 = 1 then 1 else 0 end) from tob_stavke_rac;
SUM(CASEWHEN1=1THEN1ELSE0END)
-----------------------------
53195373
Execution Plan
----------------------------------------------------------
Plan hash value: 3099656827
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30846 (2)| 00:00:02 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FAST FULL SCAN| SRC_S_STA_FK_I | 53M| 30846 (2)| 00:00:02 |
--------------------------------------------------------------------------------
SQL>
在该数据库中,似乎没有任何优势。也许在Oracle 8i中就是这种情况(截至今天,这是一款已有20年历史的软件),但今天,在12c版本中,我不会这么说。此外,如果优化程序得出结论 - 如果重新编写 - 它可以 citius,altius,fortius (向South Corea的奥运会致敬),Oracle倾向于重写查询。
[编辑,显示解释计划在RBO中的表现]
正如您所见,有些信息遗失了......
SQL> select count(*) from tob_stavke_rac;
COUNT(*)
----------
53195373
Execution Plan
----------------------------------------------------------
Plan hash value: 3371741006
---------------------------------------------
| Id | Operation | Name |
---------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | SORT AGGREGATE | |
| 2 | TABLE ACCESS FULL| TOB_STAVKE_RAC |
---------------------------------------------
Note
-----
- rule based optimizer used (consider using cbo)
SQL>