PostgreSQL'de bir tablonun satır sayısını keşfetmenin hızlı yolu


110

Yüzdeyi hesaplamak için tablodaki satır sayısını bilmem gerekiyor. Toplam sayı önceden tanımlanmış bir sabitten büyükse, sabit değeri kullanacağım. Aksi takdirde, gerçek satır sayısını kullanacağım.

Kullanabilirim SELECT count(*) FROM table. Ancak sabit değerim 500.000 ise ve tablomda 5.000.000.000 satır varsa , tüm satırları saymak çok zaman kaybedecektir.

Sabit değerim aşılır aşılmaz saymayı durdurmak mümkün mü?

Yalnızca verilen sınırın altında olduğu sürece tam satır sayısına ihtiyacım var. Aksi takdirde, sayı limitin üzerindeyse bunun yerine limit değerini kullanıyorum ve cevabı olabildiğince hızlı istiyorum.

Bunun gibi bir şey:

SELECT text,count(*), percentual_calculus()  
FROM token  
GROUP BY text  
ORDER BY count DESC;

5
N = sabit + 1 olan ilk n satırı seçmeye çalışamaz mısınız? Sabit değerinizden daha fazlasını döndürürse, sabitinizi kullanmanız gerektiğini biliyorsunuz ve değilse iyi değil misiniz?
gddc

Tabloda bir kimlik veya otomatik artış alanınız var mı
Sparky

1
@Sparky: Sıra destekli PK'lerin bitişik olması garanti edilmez, satırlar silinebilir veya iptal edilen işlemlerin neden olduğu boşluklar olabilir.
mu çok kısa

Güncellemeniz orijinal sorunuzla çelişiyor gibi görünüyor ... tam satır sayısını bilmeniz mi gerekiyor, yoksa yalnızca bir eşiğin altındaysa tam sayıyı bilmeniz mi gerekiyor?
Flimzy

1
@ RenatoDinhaniConceição: Çözmeye çalıştığınız problemi tam olarak açıklayabilir misiniz? Aşağıdaki cevabımın başlangıçta sorununun olduğunu söylediğini çözdüğünü düşünüyorum. Güncelleme, sayım (*) ve diğer birçok alan istediğiniz gibi görünmesini sağlar. Ne yapmaya çalıştığınızı tam olarak açıklayabilmeniz yardımcı olacaktır. Teşekkürler.
Ritesh

Yanıtlar:


229

Büyük tablolardaki satırları saymanın PostgreSQL'de yavaş olduğu bilinmektedir. Kesin bir sayı elde etmek için, MVCC'nin doğası gereği tam bir satır sayımı yapması gerekir . Sizin durumunuzda olduğu gibi sayımın kesin olması gerekmiyorsa , bunu çarpıcı bir şekilde hızlandırmanın bir yolu var .

Tam sayıyı almak yerine ( büyük tablolarda yavaş ):

SELECT count(*) AS exact_count FROM myschema.mytable;

Bunun gibi yakın bir tahmin elde edersiniz ( son derece hızlı ):

SELECT reltuples::bigint AS estimate FROM pg_class where relname='mytable';

Tahminin ne kadar yakın olduğu, ANALYZEyeterince koşup koşmamanıza bağlıdır . Genellikle çok yakındır. PostgreSQL Wiki SSS
bölümüne bakın . Veya count (*) performansı için ayrılmış wiki sayfası .

Daha iyisi

PostgreSQL Wiki makale edilir oldu biraz özensiz . Bir veritabanında - farklı şemalarda - aynı isimde birden çok tablo olma olasılığını göz ardı etti. Bunu hesaba katmak için:

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'

Ya da daha iyisi

SELECT reltuples::bigint AS estimate
FROM   pg_class
WHERE  oid = 'myschema.mytable'::regclass;

Daha hızlı, daha basit, daha güvenli, daha zarif. Nesne Tanımlayıcı Türleri hakkındaki kılavuza bakın .

to_regclass('myschema.mytable')Geçersiz tablo adlarının istisnalarını önlemek için Postgres 9.4+ ile kullanın :


TABLESAMPLE SYSTEM (n) Postgres 9.5+ sürümünde

SELECT 100 * count(*) AS estimate FROM mytable TABLESAMPLE SYSTEM (1);

Gibi @a_horse yorumladı , için yeni eklenen fıkra SELECTistatistik eğer komuta yararlı olabilir pg_classnedense değil şimdiki yeterlidir. Örneğin:

  • Koşmak yok autovacuum.
  • Hemen sonra büyük INSERTveya DELETE.
  • TEMPORARYtablolar (kapsamına girmeyen autovacuum).

Bu yalnızca rastgele n % ( 1örnekte) blok seçimine bakar ve içindeki satırları sayar. Daha büyük bir numune maliyeti artırır ve seçiminizdeki hatayı azaltır. Doğruluk daha fazla faktöre bağlıdır:

  • Sıra boyutunun dağılımı. Belirli bir blok normal satırlardan daha geniş tutarsa, sayı normalden daha düşüktür vb.
  • Ölü demetler veya FILLFACTORblok başına yer kaplar. Tabloya eşit olmayan bir şekilde dağıtılırsa, tahmin yanlış olabilir.
  • Genel yuvarlama hataları.

Çoğu durumda, tahmin pg_classdaha hızlı ve daha doğru olacaktır.

Asıl soruya cevap

İlk olarak, bu tablodaki satırların sayısını bilmem gerekiyor, eğer toplam sayı önceden tanımlanmış bir sabitten büyükse,

Ya da ...

... sayım sabit değerimi geçtiği anda mümkündür, saymayı durdurur (ve satır sayısının daha büyük olduğunu bildirmek için saymanın bitmesini beklemeyin).

Evet. Aşağıdakilerle bir alt sorguLIMIT kullanabilirsiniz :

SELECT count(*) FROM (SELECT 1 FROM token LIMIT 500000) t;

Postgres aslında verilen sınırın ötesinde saymayı durdurur , n satıra kadar (örnekte 500000) kesin ve güncel bir sayım elde edersiniz , aksi takdirde n . Yine de tahmin edilen kadar hızlı değil .pg_class


8
Sonunda Postgres Wiki sayfasını iyileştirilmiş sorgu ile güncelledim.
Erwin Brandstetter

5
9.5 ile hızlı bir tahmin almak şu tablesamplecümle kullanılarak mümkün olmalıdır : örneğinselect count(*) * 100 as cnt from mytable tablesample system (1);
a_horse_with_no_name

1
@JeffWidman: Bu tahminlerin tümü, çeşitli nedenlerle gerçek satır sayısından daha büyük olabilir . En önemlisi, bu arada silinmeler olmuş olabilir.
Erwin Brandstetter

2
@ErwinBrandstetter bu sorunun eski olduğunu fark eder, ancak sorguyu alt sorguya sardıysanız, sınır bu yine de etkili olur mu yoksa tüm alt sorgu yürütülecek ve ardından dış sorguda sınırlandırılır. SELECT count(*) FROM (Select * from (SELECT 1 FROM token) query) LIMIT 500000) limited_query;(Soruyorum çünkü keyfi bir sorgudan zaten bir sınır cümlesi olabilecek bir sayı almaya çalışıyorum)
Nicholas Erdenberger

1
@NicholasErdenberger: Bu, alt sorguya bağlıdır. Postgres'in yine de sınırdan daha fazla satırı dikkate alması gerekebilir ( ORDER BY somethingbir dizin kullanamadığında veya toplama işlevlerinde olduğu gibi). Bunun dışında, alt sorgudan yalnızca sınırlı sayıda satır işlenir.
Erwin Brandstetter

12

Bunu bir postgres uygulamasında şunu çalıştırarak yaptım:

EXPLAIN SELECT * FROM foo;

Daha sonra çıktıyı bir normal ifade veya benzer bir mantıkla inceleme. Basit bir SEÇİM * için, çıktının ilk satırı şunun gibi görünmelidir:

Seq Scan on uids  (cost=0.00..1.21 rows=8 width=75)

rows=(\d+)Değeri, döndürülecek satır sayısının kabaca bir tahmini olarak kullanabilirsiniz , o zaman yalnızca SELECT COUNT(*)tahmini eşiğin 1,5 katından azsa (veya uygulamanız için mantıklı olduğunu düşündüğünüz sayı) gerçek olanı yapabilirsiniz.

Sorgunuzun karmaşıklığına bağlı olarak, bu sayı giderek daha az doğru hale gelebilir. Aslında, uygulamamda, birleşimler ve karmaşık koşullar ekledikçe, o kadar yanlış hale geldi ki, 100 kuvvet içinde kaç satır geri döneceğimizi bilmek bile tamamen değersizdi, bu yüzden bu stratejiyi terk etmek zorunda kaldık.

Ancak sorgunuz Pg'nin makul bir hata payı dahilinde kaç satır döneceğini tahmin edebilecek kadar basitse, sizin için işe yarayabilir.


2

Bu Blogdan referans alınmıştır.

Satır sayısını bulmak için sorgulamak için aşağıyı kullanabilirsiniz.

Pg_class kullanarak:

 SELECT reltuples::bigint AS EstimatedCount
    FROM   pg_class
    WHERE  oid = 'public.TableName'::regclass;

Pg_stat_user_tables kullanarak:

SELECT 
    schemaname
    ,relname
    ,n_live_tup AS EstimatedCount 
FROM pg_stat_user_tables 
ORDER BY n_live_tup DESC;

Bu yöntemin çalışması için tablolarınızı VAKUM ANALİZİ yapmanız gerektiğini hızlıca not edin.
William ABMA

1

Oracle'da, rownumdöndürülen satır sayısını sınırlamak için kullanabilirsiniz . Diğer SQL'lerde de benzer yapının var olduğunu tahmin ediyorum. Dolayısıyla, verdiğiniz örnek için, döndürülen satır sayısını 500001 ile sınırlayabilir ve count(*)ardından bir uygulayabilirsiniz :

SELECT (case when cnt > 500000 then 500000 else cnt end) myCnt
FROM (SELECT count(*) cnt FROM table WHERE rownum<=500001)

1
SELECT count (*) cnt FROM tablodan her zaman tek bir satır döndürür. LIMIT'in oraya nasıl bir fayda sağlayacağından emin değilim.
Chris Bednarski

@ChrisBednarski: Cevabımın oracle sürümünü bir Oracle veritabanında doğruladım. Harika çalışıyor ve OP'nin problemi olduğunu düşündüğüm şeyi çözüyor ( count(*)rownum ile 0.05 s , rownum kullanmadan 1 s). Evet, SELECT count(*) cnt FROM tableher zaman 1 satır döndürecektir, ancak SINIR koşulu ile, tablo boyutu 500000'in üzerinde olduğunda "500001" ve tablo boyutu <= 500000 olduğunda <boyut> döndürür.
Ritesh

2
PostgreSQL sorgunuz tamamen saçmadır. Sözdizimsel ve mantıksal olarak yanlış. Lütfen düzeltin veya kaldırın.
Erwin Brandstetter

@ErwinBrandstetter: Kaldırıldı, PostgreSQL'in bu kadar farklı olduğunu fark etmemiştim.
Ritesh

@allrite: Oracle sorgunuzun sorunsuz çalıştığına şüphe yok. LIMIT farklı çalışıyor. Temel düzeyde, veritabanı motoru tarafından sorgulanan satır sayısını değil, istemciye döndürülen satır sayısını sınırlar.
Chris Bednarski

0

Metin sütunu ne kadar geniş?

GROUP BY ile veri taramasından kaçınmak için yapabileceğiniz pek bir şey yoktur (en azından bir dizin taraması).

Tavsiye ederim:

  1. Mümkünse, metin verilerinin tekrarını kaldırmak için şemayı değiştirin. Bu şekilde sayım, 'çok' tablosundaki dar bir yabancı anahtar alanında gerçekleşecektir.

  2. Alternatif olarak, metnin HASH'i ile oluşturulmuş bir sütun, ardından karma sütunu GRUPLA ile oluşturma. Yine, bu iş yükünü azaltmaktır (dar bir sütun indeksinde tarama yapmak)

Düzenle:

Orijinal sorunuz, düzenlemenizle tam olarak eşleşmedi. GROUP BY ile kullanıldığında COUNT öğesinin tüm tablodaki öğe sayısını değil, grup başına öğe sayısını döndüreceğinin farkında olup olmadığınızı bilmiyorum.


0

Sayıyı aşağıdaki sorgu ile (* veya sütun adları olmadan) alabilirsiniz.

select from table_name;

2
Bu daha hızlı görünmüyor count(*).
Güneşli

-3

SQL Server (2005 veya üstü) için hızlı ve güvenilir bir yöntem:

SELECT SUM (row_count)
FROM sys.dm_db_partition_stats
WHERE object_id=OBJECT_ID('MyTableName')   
AND (index_id=0 or index_id=1);

Sys.dm_db_partition_stats ile ilgili ayrıntılar MSDN'de açıklanmaktadır

Sorgu, (muhtemelen) bölümlenmiş bir tablonun tüm bölümlerinden satırlar ekler.

index_id = 0 sıralı olmayan bir tablodur (Yığın) ve index_id = 1 sıralı bir tablodur (kümelenmiş dizin)

Daha hızlı (ancak güvenilmez) yöntemler burada ayrıntılı olarak açıklanmıştır .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.