在Oracle中更快的selectSELECT COUNT(*)FROM sometable

我已经注意到,在Oracle中,查询

SELECT COUNT(*) FROM sometable; 

大桌子很慢。 它看起来像数据库,它实际上每一行,并增加一个计数器一次。 我认为在表格的某个位置会有一个表格,表格有多less行。

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

想想看:数据库真的要到每一行去做。 在多用户环境中,我的COUNT(*)可能与您的COUNT(*) 。 为每一个会话都设置一个不同的计数器是不切实际的,所以你必须对行进行计数。 无论如何,大多数情况下,您的查询中都会有一个WHERE子句或JOIN,因此您的假devise数器将具有较小的实用价值。

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

位图索引具有NULL行的条目,所以如果有可用的COUNT(*)将使用位图索引。

如果您只需要粗略估算,则可以从样本中推断出:

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

为了获得更高的速度(但精度较低),可以减小样本量:

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

为了获得更高的速度(但精度更高),您可以使用块式采样:

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

这对大桌子很有用。

 SELECT NUM_ROWS FROM ALL_TABLES WHERE TABLE_NAME = 'TABLE_NAME_IN_UPPERCASE'; 

对于中小尺寸的桌子,以下是可以的。

 SELECT COUNT(Primary_Key) FROM table_name; 

干杯,

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

这是一张桌子…

 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条目的列上创build索引…

 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列上构build索引….

 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> 

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

 create index idx on t(0); 

这可以被扫描以进行计数。

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

选项3:对于大表快速估计调用SAMPLE子句…例如…

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

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

呃…

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

例:

 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; 

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

计算表的最快方法正是你所做的。 Oracle并不知道你可以做什么。

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

例如:

  1. 你有没有至less做过一个解释计划,看看甲骨文在做什么?
  2. 这张桌子里有几行?
  3. 你使用的是什么版本的Oracle? 8,9,10,11 … 7?
  4. 你有没有在这个表上运行数据库统计?
  5. 这是一个频繁更新的表或批量加载或只是静态数据?
  6. 这是唯一缓慢的COUNT(*)吗?
  7. SELECT COUNT(*)FROM Dual需要多长时间?

我承认我41秒不会高兴,但真的为什么你认为它应该更快? 如果你告诉我们这个桌子有180亿行,并且在2001年从一个车库出售买来的笔记本电脑上运行,那么除非你有更好的硬件,否则41秒可能不是那么远。 但如果你说你在Oracle 9上,而且你去年夏天运行统计,那么你可能会得到不同的build议。

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 

您可以改用COUNT(1)