【问题标题】:Always true conditional aggregate vs count始终为真条件聚合与计数
【发布时间】:2018-02-09 22:25:10
【问题描述】:

我遇到过使用 1=1 条件聚合而不是计数的编码

select sum(case when 1=1 then 1 else 0 end)

在我看来,这相当于:

select count(1)

起初我认为这是一个占位符,开发人员打算在此条件聚合中使用一些额外的逻辑重新访问,但后来我在另一个脚本中找到它,然后在另一个脚本中找到它。这促使我四处打听,我发现以前的开发人员曾强调,这种求和条件在所有情况下都比计数函数更有效和更快(强调得足够好,以至于其他开发人员后来都遵循了标准)。对我来说似乎很反直觉,强制系统评估 1=1 条件可能是次要的,但它仍然是 count 函数的额外工作。我想在我回来发表坚定的声明之前先在这里咨询一下。

a) 这个开发人员所说的条件聚合将比计数快

完全偏题: b) 是否有一个数据库系统可以比计数更快地评估条件聚合?

这是一个 oracle 11g 数据库,虽然我怀疑这些脚本是为 oracle 8i 编写的

作为奖励积分...我被要求优化此代码。用计数功能替换它会提高速度吗?记录数有时超过 1 亿。

【问题讨论】:

    标签: sql oracle


    【解决方案1】:

    执行摘要:它没有任何区别,而且在 Oracle 中从来没有,至少从版本 6 (1989) 开始,我第一次开始听说通过选择主键列等来加快计数的聪明方法,就好像 Oracle 是不知道人们有时会计算事物。

    您可以通过在过滤器中使用表达式并检查执行计划的“谓词”部分来查看解析器/优化器对表达式的作用。

    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)
    

    请注意谓词部分,该部分显示sum 表达式已被计算并替换为sum(1)。 (我现在没有时间深入研究跟踪文件,但我很确定它们会表明重写发生在 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
    

    【讨论】:

      【解决方案2】:

      我想,找到答案的最简单方法是解释这两个选项,然后看看 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 with 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 版本中,我不会这么说。此外,Oracle 倾向于重写查询优化器是否得出结论 - 如果重写 - 它可以工作citius, altius, fortius(向南朝鲜奥运会致敬)。 p>

      [编辑,显示解释计划在 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>
      

      【讨论】:

      • 出于兴趣,9i choose 的提示是什么?
      • 出于某种原因(阅读:因为 someone 是这样说的,字面意思 - 至少,当我询问时,我被告知是这样的),该数据库被设置为使用RBO 所以解释计划说,嗯,正在使用 RBO,我应该考虑改用 CBO。由于我做不到,我有点强制 Oracle 选择执行该查询的内容和方式。我可以采取不同的做法吗?
      • 它真的改变了计划吗?有趣的是,我在 Oracle 9.2 之后的任何版本中都找不到它的文档,并且 optimizer_mode 初始化参数有一段时间没有 choose 选项(9.210.1),所以它似乎是与古老过时功能相关的无证提示。
      • @William,我编辑了我的消息并添加了没有这样的提示的解释。
      • 哇。这是什么版本的Oracle,你知道是哪个初始化参数负责吗?
      猜你喜欢
      • 2021-12-04
      • 2014-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多