我注意到在Oracle中,查詢更快的替代SELECT COUNT(*)FROM sometable
SELECT COUNT(*) FROM sometable;
是大表非常慢。它看起來像數據庫實際上遍歷每一行,並一次遞增一個計數器。我認爲在表格的某個位置會有一個計數器,表中有多少行。
因此,如果我想檢查Oracle中表中的行數,那麼最快的方法是什麼?
我注意到在Oracle中,查詢更快的替代SELECT COUNT(*)FROM sometable
SELECT COUNT(*) FROM sometable;
是大表非常慢。它看起來像數據庫實際上遍歷每一行,並一次遞增一個計數器。我認爲在表格的某個位置會有一個計數器,表中有多少行。
因此,如果我想檢查Oracle中表中的行數,那麼最快的方法是什麼?
想一想:數據庫真的必須去每一行來做到這一點。 在多用戶環境中,我的COUNT(*)
可能與您的COUNT(*)
不同。爲每一個會話都設置一個不同的計數器是不切實際的,所以你可以從字面上計算行數。無論如何,大多數情況下,您的查詢中都會有一個WHERE子句或一個JOIN,因此您的假設計數器將具有較低的實用價值。
然而,有些方法可以加快速度:如果您在NOT NULL列上有INDEX,Oracle將計算索引的行數而不是表的數量。在適當的關係模型中,所有表都有一個主鍵,因此COUNT(*)
將使用主鍵的索引。
位圖索引具有空行的條目,所以如果有可用的COUNT(*)將使用位圖索引。
我在這個表上的4個列上有一個主鍵,每個列都不是NULL,並且還需要41秒來完成一個COUNT(*)。該索引是否必須位於單列上才能快速生成COUNT(*)查詢? – 2009-12-03 15:50:28
@Eli:索引鍵越小,查詢就越快。如果你的桌子很大,那麼COUNT(*)'無論如何都需要時間。在其他答案中看到一些對COUNT(*)的替代方法的建議(在一個實例化視圖中記錄結果,在一個樣本上記下COUNT,...) – 2009-12-03 16:13:30
爲了記錄,我看到了一個簡單的SELECT(*)多達40分鐘。在具有4個非空數字列和844 mio主鍵的表上。條目。 – 2017-02-13 16:54:17
如果表在NOT NULL列上有索引,COUNT(*)將使用該索引。否則它執行全表掃描。請注意,索引不一定是UNIQUE,它只是非空。
下面是一張桌子
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>
選項1:對可用於掃描的非空列存在索引。或者創建一個基於函數的索引:
create index idx on t(0);
然後可以掃描這個計數。選項2:如果您啓用了監控,請檢查監控視圖USER_TAB_MODIFICATIONS並將相關值添加/減去表格統計信息。
方案3:有關大表可以快速估計調用樣本條款......比如......
SELECT 1000*COUNT(*) FROM sometable SAMPLE(0.1);
選項4:使用物化視圖保持COUNT(*)。雖然功能強大。
...
您可以創建快速刷新物化視圖來存儲計數。
例子:
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;
它會減慢對錶突變sometable了一點,但計數會變得快了很多。
如果你想只是一個粗略的估計,你可以從一個樣本推斷:
SELECT COUNT(*) * 100 FROM sometable SAMPLE (1);
得到更快的速度(但精度較低),可以減少試樣尺寸:
SELECT COUNT(*) * 1000 FROM sometable SAMPLE (0.1);
對於更高的速度(但更差的精度),您可以使用塊式採樣:
SELECT COUNT(*) * 100 FROM sometable SAMPLE BLOCK (1);
是否可以在子查詢上做到這一點? – uesports135 2016-05-02 17:05:24
不,不幸的是,SAMPLE子句僅適用於基表。 – 2016-05-03 12:14:46
你能想到任何其他的方式來實現一個子查詢類似的概念? – uesports135 2016-05-03 15:09:02
獲得一張表的最快方法正是你所做的。 Oracle不知道你可以做什麼。
有些東西你沒有告訴我們。即爲什麼你認爲這應該更快?
例如:
我承認我不會快樂41秒,但真的,爲什麼你認爲它應該是更快?如果你告訴我們這張桌子有180億行,並且正在2001年從一個車庫出售中購買的筆記本電腦上運行,那麼除非你有更好的硬件,否則41秒可能不會那麼遠。但是如果你說你在Oracle 9上,並且你在去年夏天運行統計,那麼你可能會得到不同的建議。
其他答案指出還有其他更快的方法來獲取行數。包括甲骨文內部的可疑計數器。只是不如計數(*)準確。 – 2015-11-06 19:45:39
這對於大型表格非常適用。
SELECT NUM_ROWS FROM ALL_TABLES WHERE TABLE_NAME = 'TABLE_NAME_IN_UPPERCASE';
對於中小尺寸的桌子,以下是可以的。
SELECT COUNT(Primary_Key) FROM table_name;
乾杯,
確保'table_name'的格式是''TABLE_NAME'' – imanuelcostigan 2013-02-15 02:08:10
這不會實時更新,根據您的數據庫不同,可能根本不會更新。 http://docs.oracle.com/cd/B28359_01/server.111/b28320/statviews_2105.htm#REFRN20286 – Joseph 2013-12-10 18:19:48
你可以使用COUNT(1),而不是
請注意與此[刪除的答案](http://stackoverflow.com/a/10335036/)相關的註釋(需要10K代表才能看到它)。註釋說'COUNT(1)'和'COUNT(*)'被視爲相同 - 在任何SQL數據庫中通常都是如此。這是一個「城市神話」,有差異(或者如果DBMS存在差異,DBMS在其優化器中有嚴格的監督;應該沒有區別)。 – 2016-09-06 06:37:20
有來自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
另外,您在逐字引用「詢問湯姆」的答案時,並未明確表示您正在這樣做。這並不是真正遵循SO的規則,至少需要這樣的確認 - 比您的原始版本的答案更明確,甚至比我在查看Ask Tom頁面之前編輯的版本更加明確。 – 2016-09-06 05:02:50
好的;我修改了頁面,所以很明顯這基本上是Ask Tom答案的逐字拷貝。現在剩下的問題給你 - [m.r226](http://stackoverflow.com/users/5516527/m-r226) - 你是否真的有權制作答案的副本並重新發布?如果不是,那麼你違反了版權法,這對於SO來說是有問題的。 – 2016-09-06 05:13:15
這裏的大量信息是由[Jeffrey Kemp](http://stackoverflow.com/users/103295/jeffrey-kemp)在他的[答案](http://stackoverflow.com/a/1844985)中提供的/ 15168)from 2009. – 2016-09-06 05:15:56
的目的是什麼,你檢查的行數?這是你特設的東西,例如在SQLDeveloper或SQL * PLUS中用於您自己的目的,或者這將成爲程序的一部分,例如獲得行數,以便您可以顯示「345頁的第1頁」或其他內容?你需要確切的數量,還是近似工作? – shoover 2009-12-03 19:06:57
順便說一句,我想如果你只是需要一個近似值,這是你臨時做的事情,那麼如果你最近分析了你的表,你可以選擇NUM_ROWS FROM USER_TABLES WHERE TABLE_NAME ='SOME_TABLE'; – shoover 2009-12-03 19:29:14
謝謝,那正是我所需要的,因爲正如你所猜測的,我通常只是在尋找一個近似值。 – 2010-01-07 22:18:40