sql - WHERE 与 HAVING 性能与 GROUP BY

标签 sql oracle performance where having

所以我被分配去估计两个查询的性能并得出了一个令人惊讶的结果。我事先被告知 HAVINGWHERE 慢因为它只在访问行后过滤结果。这似乎很合理,而且this question on SQL clause execution order加强了这一点。
但是,我根据一些假设估计了以下查询的性能,并且似乎使用了 HAVING执行实际上更快!

SELECT status, count(status)
FROM customer
GROUP BY status
HAVING status != 'Active' AND status != 'Dormant'

SELECT status, count(status)
FROM customer
WHERE status != 'Active' AND status != 'Dormant'
GROUP BY status
假设是:
  • CUSTOMER有 100 000 条记录
  • 访问一行的成本是 0.01ms (SELECT + COUNT)
  • 执行一个子句的成本是 0.005ms
  • 客户状态分为三种类型,上述两种和“已故”
  • 有 15 000 个“已故”客户

  • 基于此,我的估计是:
    First query:
        Accessing all rows, FROM: 100 000 * 0.01ms = 1000ms
        GROUP BY: 100 000 * 0.005ms = 500ms
        HAVING (2 conditions, 3 groups): 2 * 3 * 0.005ms = 0.03ms
        SELECT and COUNT results: 15 000 * 0.01ms = 150ms
        Total execution time: 1.65003s
    
    Second query:
        Accessing all the rows, FROM: 1000ms
        WHERE: 2 * 100 000 * 0.005ms = 1000ms
        GROUP BY: 15 000 * 0.005ms = 75ms
        SELECT and COUNT results: 15 000 * 0.01ms = 150ms
        Total execution time: 2.225s
    
    结果来自GROUP BY只产生三组,很容易过滤,而WHERE必须一一浏览和过滤记录。
    由于我天真地依赖权威,我假设我要么在某处犯了错误,要么提供的假设是错误的。
    GROUP BY也是如此用 HAVING 表现得像这样导致执行时间减少?
    编辑:查询计划
    PLAN_TABLE_OUTPUT /* With HAVING */
    
    | Id  | Operation                   | Name | Rows  | Bytes | Cost (%CPU)| Time     |
    ------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT            |      |     5 |    35 |     4  (25)| 00:00:01 |
    |*  1 |  FILTER                     |      |       |       |            |          |
    |   2 |   HASH GROUP BY             |      |     5 |    35 |     4  (25)| 00:00:01 |
    |   3 |    TABLE ACCESS STORAGE FULL| CUSM |     5 |    35 |     3   (0)| 00:00:01 |
    ------------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    1 - filter("STATUS"<>'Active' AND "STATUS"<>'Dormant')
    
    
    PLAN_TABLE_OUTPUT /* With WHERE */
    -----------------------------------------------------------------------------------
    | Id  | Operation                  | Name | Rows  | Bytes | Cost (%CPU)| Time     |
    -----------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT           |      |     1 |     7 |     4  (25)| 00:00:01 |
    |   1 |  HASH GROUP BY             |      |     1 |     7 |     4  (25)| 00:00:01 |
    |*  2 |   TABLE ACCESS STORAGE FULL| CUSM |     1 |     7 |     3   (0)| 00:00:01 |
    -----------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    2 - storage("STATUS"<>'Active' AND "STATUS"<>'Dormant')
        filter("STATUS"<>'Active' AND "STATUS"<>'Dormant')
    

    最佳答案

    事情是这样的:

  • 根据Oracle执行计划,两个查询都在执行全表扫描。也就是说,他们正在阅读 所有行 表的。那里没有区别。
  • HAVING 查询执行 GROUP BY(散列),结果为 3 行。然后,它将过滤器应用于这 3 行,并返回结果。
  • WHERE 查询在读取后将过滤器应用于每一行(规范中的 100,000),将它们减少到 15,000。最后,它将这些(散列)分组为 1 行,并返回一行。

  • 我认为在您所描述的情况下,WHERE 查询将过滤器应用于所有 100,000 行,而 HAVING 查询推迟过滤器并仅将其应用于 3 行。这使得 HAVING 查询更快。

    现在,不要假设此结果将适用于您这样的每个查询。 Oracle 在使用表统计信息方面非常聪明。计划将来会根据您添加到表中的真实数据而改变。 5 行的计划绝不代表 100,000 行的计划。

    对这个结果持保留态度。现实世界的场景要复杂得多。

    关于sql - WHERE 与 HAVING 性能与 GROUP BY,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49758446/

    相关文章:

    php - mysql高效查询(选择和更新)

    sql - 每个 DISTINCT 的多个 ORDER BY 和 LIMIT

    sql - 如何将子查询where条件移出子查询?

    performance - 已编入索引的 GL_TRIANGLES 与编入索引的 GL_TRIANGLE_STRIP

    c - 在 C 中进行二进制算术的最佳方法?

    mysql - 左外连接有多个匹配项,如何返回特定的一个?

    sql - 奖励符合条件的产品

    sql-server - 比较具有相同模式的两个表的数据差异

    java - 如何从 IntelliJ IDEA 14 连接到 Oracle 数据库?

    javascript - 强制在 Mobile Safari 上呈现整页?