Oracle中更快的替代方法是SELECT COUNT(*)FROM sometable

时间:2009-12-03 15:18:41

标签: oracle count

我在Oracle中注意到了查询

SELECT COUNT(*) FROM sometable;

对于大型表来说非常慢。它似乎是数据库,它实际上遍历每一行并一次递增一个计数器。我认为表中某处有一个计数器表有多少行。

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

11 个答案:

答案 0 :(得分:53)

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

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

为了提高速度(但精度较低),您可以减小样本量:

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

如果速度更快(但精度更差),您可以使用逐块采样:

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

答案 1 :(得分:44)

这适用于大型表格。

SELECT NUM_ROWS FROM ALL_TABLES WHERE TABLE_NAME = 'TABLE_NAME_IN_UPPERCASE';

对于中小型表,以下都可以。

SELECT COUNT(Primary_Key) FROM table_name;

干杯,

答案 2 :(得分:27)

想一想:数据库真的必须去每一行才能做到这一点。 在多用户环境中,我的COUNT(*)可能与您的COUNT(*) 不同。为每个会话设置一个不同的计数器是不切实际的,因此你可以按字面计算行数。大多数情况下,你的查询中都会有WHERE子句或JOIN,所以你的假设计数器具有实用价值。

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

位图索引具有NULL行的条目,因此如果有一个位图索引,COUNT(*)将使用位图索引。

答案 3 :(得分:13)

如果表在NOT NULL列上有索引,则COUNT(*)将使用该列。否则,它将执行全表扫描。请注意,索引不必是UNIQUE,它必须是非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>

答案 4 :(得分:7)

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

create index idx on t(0);

然后可以扫描这个以计算。

选项2:如果您已启用监控,请检查监控视图USER_TAB_MODIFICATIONS并将相关值添加/减去表统计信息。

选项3:对大型表的快速估计调用SAMPLE子句...例如......

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

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

嗯......

答案 5 :(得分:5)

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

示例:

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; 

它可以减缓桌面上的突变,但计数会变得更快。

答案 6 :(得分:3)

获得桌子计数的最快方法正是你所做的。你可以做的任何技巧都是Oracle不知道的。

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

例如:

  1. 你至少做过一个解释计划,看看Oracle在做什么吗?
  2. 此表中有多少行?
  3. 您使用的是哪个版本的Oracle? 8,9,10,11 ... 7?
  4. 您是否曾在此表上运行数据库统计信息?
  5. 这是经常更新的表或批量加载还是只是静态数据?
  6. 这是你唯一的慢COUNT(*)吗?
  7. SELECT COUNT(*)FROM Dual需要多长时间?
  8. 我承认我会在41秒内不开心,但为什么你觉得它应该更快?如果你告诉我们这张桌子有180亿行并且是你在2001年从车库出售的笔记本电脑上运行的话,41秒可能并不是那么好“除非你得到更好的硬件”。但是,如果你说你使用的是Oracle 9并且你去年夏天运行了统计数据,你可能会得到不同的建议。

答案 7 :(得分:1)

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

  

如果您有足够的服务器电源,则可以执行

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

如果您刚刚接近,可以这样做:

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

另外,如果有

     
      
  1. 一个不包含空值的列,但未定义为NOT NULL和
  2.   
  3. 该列上有索引
  4.         你可以尝试:

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

答案 8 :(得分:0)

这对我来说很好

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

答案 9 :(得分:-1)

使用以下方法可以获得更好的性能:

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

答案 10 :(得分:-4)

您可以使用COUNT(1)代替