总是真正的条件聚合与计数

时间:2018-02-09 22:25:10

标签: sql oracle

我遇到过使用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亿。

2 个答案:

答案 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>