在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并不知道你可以做什么。
有些事情你没有告诉我们。 就是为什么你认为这应该更快?
例如:
- 你有没有至less做过一个解释计划,看看甲骨文在做什么?
- 这张桌子里有几行?
- 你使用的是什么版本的Oracle? 8,9,10,11 … 7?
- 你有没有在这个表上运行数据库统计?
- 这是一个频繁更新的表或批量加载或只是静态数据?
- 这是唯一缓慢的COUNT(*)吗?
- 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);
另外,如果有的话
- 不包含空值但未定义为NOT NULL的列
- 该列上有一个索引
你可以尝试:
select /*+ index_ffs(t) */ count(*) from sometable t where indexed_col is not null
您可以改用COUNT(1)