3 回答

TA貢獻1820條經驗 獲得超10個贊
在PostgreSQL中,對大表中的行進行計數是很慢的。為了獲得準確的數字,由于MVCC的性質,它必須對行進行完整計數。有一種方法來大大加快這如果計數也沒有必須要確切喜歡它似乎是在你的情況。
而不是獲得確切的計數(大表速度慢):
SELECT count(*) AS exact_count FROM myschema.mytable;
您會得到如下估算值(非??欤?/p>
SELECT reltuples::bigint AS estimate FROM pg_class where relname='mytable';
估算的接近程度取決于您是否運行ANALYZE足夠。通常非常接近。
請參閱PostgreSQL Wiki FAQ。
或用于count(*)性能的專用Wiki頁面。
更好了
PostgreSQL的維基文章的是一個有點草率。它忽略了一個數據庫中可能存在多個具有相同名稱的表的可能性-處于不同的模式。要說明這一點:
SELECT c.reltuples::bigint AS estimate
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = 'mytable'
AND n.nspname = 'myschema'
還是更好
SELECT reltuples::bigint AS estimate
FROM pg_class
WHERE oid = 'myschema.mytable'::regclass;
更快,更簡單,更安全,更優雅。請參閱《對象標識符類型》手冊。
to_regclass('myschema.mytable')在Postgres 9.4+中使用可避免無效表名的例外情況:
如何檢查給定架構中是否存在表
TABLESAMPLE SYSTEM (n) 在Postgres 9.5+
SELECT 100 * count(*) AS estimate FROM mytable TABLESAMPLE SYSTEM (1);
就像@a_horse commented一樣,SELECT如果pg_class由于某些原因當前的統計信息不足,則為該命令新添加的子句可能會很有用。例如:
沒有autovacuum運行。
大INSERT或DELETE。
TEMPORARY表格(未被涵蓋autovacuum)。
這只會查看隨機的n%(1在示例中)選擇的塊并計算其中的行。您可以選擇更大的樣本來增加成本,并減少誤差。準確性取決于更多因素:
行大小分布。如果給定的塊恰好比平常的行寬,則計數比平常的低,等等。
死元組或FILLFACTOR每個塊占用空間。如果整個表分布不均,則估計值可能會不正確。
一般舍入錯誤。
在大多數情況下,來自的估計pg_class會更快,更準確。
回答實際問題
首先,我需要知道該表中的行數,如果總計數大于某個預定義常量,
以及是否...
...在計數超過我的常數值時是可能的,它將停止計數(而不是等待完成計數以告知行計數更大)。
是。您可以將子查詢與結合使用LIMIT:
SELECT count(*) FROM (SELECT 1 FROM token LIMIT 500000) t;
Postgres 實際上停止計數超過給定的限制,您將獲得多達n行(在本示例中為500000)的精確和當前計數,否則為n。但是,速度不及中的估算速度。pg_class

TA貢獻1850條經驗 獲得超11個贊
我通過運行在postgres應用程序中執行了一次:
EXPLAIN SELECT * FROM foo;
然后使用正則表達式或類似的邏輯檢查輸出。對于簡單的SELECT *,輸出的第一行應如下所示:
Seq Scan on uids (cost=0.00..1.21 rows=8 width=75)
您可以將該rows=(\d+)值用作將要返回的行數的粗略估計,然后僅SELECT COUNT(*)在估計值小于閾值的1.5倍(或您認為對應用程序有意義的任何數目)時才進行實際計算。
根據查詢的復雜程度,此數字可能會越來越少。實際上,在我的應用程序中,當我們添加聯接和復雜條件時,它變得如此不準確,以至于完全不值錢,甚至不知道在100的冪內將返回多少行,因此我們不得不放棄該策略。
但是,如果您的查詢足夠簡單,Pg可以在合理的誤差范圍內預測它將返回多少行,那么它可能對您有用。

TA貢獻1799條經驗 獲得超8個贊
在Oracle中,您可以rownum用來限制返回的行數。我猜想其他SQL中也存在類似的構造。因此,對于您給出的示例,可以將返回的行數限制為500001,count(*)然后應用a :
SELECT (case when cnt > 500000 then 500000 else cnt end) myCnt
FROM (SELECT count(*) cnt FROM table WHERE rownum<=500001)
- 3 回答
- 0 關注
- 2263 瀏覽
添加回答
舉報