【问题标题】:Faster alternative in Oracle to SELECT COUNT(*) FROM sometableOracle 中 SELECT COUNT(*) FROM sometable 的更快替代方案
【发布时间】:2010-12-22 20:52:32
【问题描述】:

我注意到在 Oracle 中,查询

SELECT COUNT(*) FROM sometable;

对于大表来说非常慢。看起来数据库实际上遍历每一行并一次增加一个计数器。我认为表格中的某处会有一个计数器,该表格有多少行。

如果我想在 Oracle 中检查表中的行数,最快的方法是什么?

【问题讨论】:

  • 您检查行数的目的是什么?这是您临时做的事情吗?在 SQLDeveloper 或 SQL*PLUS 中用于您自己的目的,或者这将成为程序的一部分,例如获取行数,以便您可以显示“第 1 页,共 345 页”或其他内容?您是否需要准确的计数,或者近似值也可以?
  • 顺便说一句,我在想如果你只需要一个近似值,这是你临时做的事情,那么如果你的表是最近分析的,你可以 SELECT NUM_ROWS FROM USER_TABLES WHERE TABLE_NAME = 'SOME_TABLE' ;
  • 谢谢,这正是我所需要的,因为正如你所猜测的那样,我通常只是在寻找一个近似值。

标签: oracle count


【解决方案1】:

这对我很有效

select owner, table_name, nvl(num_rows,-1) 
from all_tables 
--where table_name in ('cats', 'dogs')
order by nvl(num_rows,-1) desc

来自https://livesql.oracle.com/apex/livesql/file/content_EPJLBHYMPOPAGL9PQAV7XH14Q.html

【讨论】:

    【解决方案2】:

    您可以通过以下方法获得更好的性能:

    SELECT COUNT(1) FROM (SELECT /*+FIRST_ROWS*/ column_name 
    FROM table_name 
    WHERE column_name = 'xxxxx' AND ROWNUM = 1);
    

    【讨论】:

    • 看起来这个查询永远不会超过一个。
    【解决方案3】:

    这对大表非常有用。

    SELECT NUM_ROWS FROM ALL_TABLES WHERE TABLE_NAME = 'TABLE_NAME_IN_UPPERCASE';
    

    对于中小型桌子,以下就可以了。

    SELECT COUNT(Primary_Key) FROM table_name;
    

    干杯,

    【讨论】:

    • 确保table_name 的格式为'TABLE_NAME'
    • 这不是实时更新的,根据您的数据库可能根本不会更新。 docs.oracle.com/cd/B28359_01/server.111/b28320/…
    • 对于一个有 8500 万行的表,从 ALL_TABLES 获取计数需要 3 毫秒。从 count(*) 获取计数需要 3 秒,而 count(primary_key) 需要类似的时间。
    • 对于一个有 52 行的表,ALL_TABLES、count(*) 和 count(primary_key) 花费的时间大致相同。
    【解决方案4】:

    Ask Tom 于 2016 年 4 月发布了相关答案。

    如果你有足够的服务器能力,你可以这样做

    select /*+ parallel */ count(*) from sometable
    

    如果你只是在近似之后,你可以这样做:

    select 5 * count(*) from sometable sample block (10);
    

    如果有的话

    1. 一列不包含空值,但未定义为 NOT NULL,并且
    2. 该列有索引

    你可以试试:

    select /*+ index_ffs(t) */ count(*) from sometable  t where indexed_col is not null
    

    【讨论】:

    • 另外,您逐字引用了 Ask Tom 的回答,但没有明确表示您正在这样做。这并没有真正遵循 SO 的规则,至少需要这样的确认——比你给出的答案的原始版本更明确,甚至比我在查看 Ask Tom 页面之前编辑的版本更明确。
    • 好的;我已经修改了页面,因此很明显这基本上是 Ask Tom 答案的逐字副本。现在你的剩余问题 - m.r226 - 你真的有权制作这个答案的副本并重新发布它吗?如果没有,你就侵犯了版权,这对 SO 来说是个问题。
    • 这里的相当多的信息以前是由Jeffrey Kemp 在他的answer 中从 2009 年开始提供的。
    【解决方案5】:

    您可以改用 COUNT(1)

    【讨论】:

    • 请注意与此 deleted answer 关联的 cmets(需要 10K 代表才能看到它)。 cmets 说 COUNT(1)COUNT(*) 被视为相同 — 这在任何 SQL 数据库中通常都是如此。这是一个“城市神话”,即存在差异(或者如果存在差异的 DBMS,则表示 DBMS 在其优化器中存在严重疏忽;应该没有差异)。
    【解决方案6】:

    想一想:数据库确实必须访问每一行才能做到这一点。 在多用户环境中,我的 COUNT(*) 可能与您的 COUNT(*) 不同。为每个会话设置不同的计数器是不切实际的,因此您必须逐行计数。无论如何,大多数情况下,您的查询中都会有 WHERE 子句或 JOIN,因此您的假设计数器将没有什么实际价值。

    但是有一些方法可以加快速度:如果您在 NOT NULL 列上有一个 INDEX,Oracle 将计算索引的行而不是表。在适当的关系模型中,所有表都有一个主键,因此COUNT(*) 将使用主键的索引。

    位图索引包含 NULL 行的条目,因此如果有可用的位图索引,COUNT(*) 将使用位图索引。

    【讨论】:

    • 我在这张表上的 4 列上有一个主键,每列都不是 NULL,而且它仍然需要 41 秒来执行 COUNT()。索引是否必须在单个列上才能快速进行 COUNT() 查询?
    • @Eli:索引键越小,查询越快。但是,如果您的桌子很大,COUNT(*) 无论如何都需要时间。在其他答案中查看COUNT(*) 的一些替代建议(在物化视图中记录结果,对样本进行计数,...)
    • 作为记录,我看到一个简单的 SELECT(*) 需要 40 分钟。在具有 4 个非空数字列和 844 mio 的主键的表上。条目。
    • @JRA_TLL:那是一张有点大的桌子,如果你想要一个精确的计数,需要一些时间。如果您想要估计 Jeffrey 的答案会快很多(您可以对 0.01% 的块进行抽样,并且仍然有一个具有统计代表性的数字)。
    【解决方案7】:

    获取表格计数的最快方法正是您所做的。没有 Oracle 不知道的技巧。

    有些事情你没有告诉我们。即为什么你认为这应该更快?

    例如:

    1. 您是否至少制定了一个解释计划以了解 Oracle 正在做什么?
    2. 这个表有多少行?
    3. 您使用的是什么版本的 Oracle? 8,9,10,11 ... 7?
    4. 您是否曾经在此表上运行过数据库统计信息?
    5. 这是一个经常更新的表或批量加载的数据还是只是静态数据?
    6. 这是您唯一的慢 COUNT(*) 吗?
    7. SELECT COUNT(*) FROM Dual 需要多长时间?

    我承认我不会对 41 秒感到满意,但你真的认为它应该更快吗?如果您告诉我们该表有 180 亿行并且在您于 2001 年从旧货销售中购买的笔记本电脑上运行,那么 41 秒可能与“它将得到的好”相差不远,除非您获得更好的硬件。但是,如果您说您使用的是 Oracle 9,并且您在去年夏天很好地运行了统计数据,您可能会得到不同的建议。

    【讨论】:

    • 其他答案指出还有其他更快的方法来获取行数。包括 Oracle 内部的可疑计数器。只是不如 count(*) 准确。
    【解决方案8】:

    如果您只想粗略估计,可以从样本中推断:

    SELECT COUNT(*) * 100 FROM sometable SAMPLE (1);

    为了提高速度(但准确性较低),您可以减少样本量:

    SELECT COUNT(*) * 1000 FROM sometable SAMPLE (0.1);

    为了更快的速度(但更差的准确性),您可以使用逐块采样:

    SELECT COUNT(*) * 100 FROM sometable SAMPLE BLOCK (1);

    【讨论】:

    • 是否可以在子查询中执行此操作?
    • 不,不幸的是,SAMPLE 子句仅适用于基表。
    • 你能想出其他方法在子查询上实现类似的概念吗?
    • 除了实际运行查询并计算返回的记录数之外,我能想到的唯一方法是使用 Oracle 的优化器统计信息 - 即查看解释计划顶层的基数- 但我只希望它们在一定程度上具有一定的代表性,而且我看不出这是任何用例的合理替代方案。
    【解决方案9】:

    您可以创建一个快速刷新的物化视图来存储计数。

    例子:

    create table sometable (
    id number(10) not null primary key
    , name varchar2(100) not null);
    
    create materialized view log on sometable with rowid including new values;
    
    create materialized view sometable_count
    refresh on commit
    as
    select count(*) count
    from   sometable;
    
    insert into sometable values (1,'Raymond');
    insert into sometable values (2,'Hans');
    
    commit;
    
    select count from sometable_count; 
    

    它会稍微减缓 table sometable 上的突变,但计数会变得更快。

    【讨论】:

      【解决方案10】:

      选项 1:在可用于扫描的非空列上有一个索引。或者创建一个基于函数的索引:

      create index idx on t(0);
      

      然后可以对其进行扫描以提供计数。

      选项 2:如果您打开了监控,则检查监控视图 USER_TAB_MODIFICATIONS 并将相关值添加/减去表统计信息。

      选项 3:为了快速估计大型表,请调用 SAMPLE 子句...例如 ...

      SELECT 1000*COUNT(*) FROM sometable SAMPLE(0.1); 
      

      选项 4:使用物化视图来维护计数 (*)。虽然是强效药物。

      嗯...

      【讨论】:

        【解决方案11】:

        如果表在 NOT NULL 列上有索引,则 COUNT(*) 将使用该索引。否则执行全表扫描。请注意,索引不必是唯一的,它必须是 NOT NULL。

        这是一张桌子……

        SQL> desc big23
         Name                                      Null?    Type
         ----------------------------------------- -------- ---------------------------
         PK_COL                                    NOT NULL NUMBER
         COL_1                                              VARCHAR2(30)
         COL_2                                              VARCHAR2(30)
         COL_3                                              NUMBER
         COL_4                                              DATE
         COL_5                                              NUMBER
         NAME                                               VARCHAR2(10)
        
        SQL>
        

        首先我们将在没有索引的情况下进行计数 ....

        SQL> explain plan for
          2      select count(*) from big23
          3  /
        
        Explained.
        
        SQL> select * from table(dbms_xplan.display)
          2  /
        select * from table)dbms_xplan.display)
        
        PLAN_TABLE_OUTPUT
        --------------------------------------------------------------------
        Plan hash value: 983596667
        
        --------------------------------------------------------------------
        | Id  | Operation          | Name  | Rows  | Cost (%CPU)| Time     |
        --------------------------------------------------------------------
        |   0 | SELECT STATEMENT   |       |     1 |  1618   (1)| 00:00:20 |
        |   1 |  SORT AGGREGATE    |       |     1 |            |          |
        |   2 |   TABLE ACCESS FULL| BIG23 |   472K|  1618   (1)| 00:00:20 |
        --------------------------------------------------------------------
        
        Note
        
        PLAN_TABLE_OUTPUT
        --------------------------------------------------------------------
           - dynamic sampling used for this statement
        
        13 rows selected.
        
        SQL>
        

        不,我们在可以包含 NULL 条目的列上创建索引...

        SQL> create index i23 on big23(col_5)
          2  /
        
        Index created.
        
        SQL> delete from plan_table
          2  /
        
        3 rows deleted.
        
        SQL> explain plan for
          2      select count(*) from big23
          3  /
        
        Explained.
        
        SQL> select * from table(dbms_xplan.display)
          2  /
        
        PLAN_TABLE_OUTPUT
        --------------------------------------------------------------------
        Plan hash value: 983596667
        
        --------------------------------------------------------------------
        | Id  | Operation          | Name  | Rows  | Cost (%CPU)| Time     |
        --------------------------------------------------------------------
        |   0 | SELECT STATEMENT   |       |     1 |  1618   (1)| 00:00:20 |
        |   1 |  SORT AGGREGATE    |       |     1 |            |          |
        |   2 |   TABLE ACCESS FULL| BIG23 |   472K|  1618   (1)| 00:00:20 |
        --------------------------------------------------------------------
        
        Note
        
        PLAN_TABLE_OUTPUT
        --------------------------------------------------------------------
           - dynamic sampling used for this statement
        
        13 rows selected.
        
        SQL>
        

        最后让我们在 NOT NULL 列上建立索引 ....

        SQL> drop index i23
          2  /
        
        Index dropped.
        
        SQL> create index i23 on big23(pk_col)
          2  /
        
        Index created.
        
        SQL> delete from plan_table
          2  /
        
        3 rows deleted.
        
        SQL> explain plan for
          2      select count(*) from big23
          3  /
        
        Explained.
        
        SQL> select * from table(dbms_xplan.display)
          2  /
        
        PLAN_TABLE_OUTPUT
        ---------------------------------------------------------------------
        Plan hash value: 1352920814
        
        ----------------------------------------------------------------------
        | Id  | Operation             | Name | Rows  | Cost (%CPU)| Time     |
        ----------------------------------------------------------------------
        |   0 | SELECT STATEMENT      |      |     1 |   326   (1)| 00:00:04 |
        |   1 |  SORT AGGREGATE       |      |     1 |            |          |
        |   2 |   INDEX FAST FULL SCAN| I23  |   472K|   326   (1)| 00:00:04 |
        ----------------------------------------------------------------------
        
        Note
        
        PLAN_TABLE_OUTPUT
        ----------------------------------------------------------------------
           - dynamic sampling used for this statement
        
        13 rows selected.
        
        SQL>
        

        【讨论】:

          猜你喜欢
          • 2019-01-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-12-11
          • 2012-07-05
          • 2012-01-23
          • 2013-07-13
          相关资源
          最近更新 更多