Wiadomo, że w PostgreSQL zliczanie wierszy w dużych tabelach jest powolne. Model MVCC wymaga pełnej liczby aktywnych wierszy w celu uzyskania dokładnej liczby. Istnieją obejścia, które mogą znacznie przyspieszyć to jeśli liczba nie muszą być dokładne tak jak wydaje się być w twoim przypadku.
(Pamiętaj, że nawet „dokładna” liczba jest potencjalnie martwa w momencie przybycia!)
Dokładna liczba
Powoli dla dużych tabel.
W przypadku współbieżnych operacji zapisu może być nieaktualny w momencie, gdy go otrzymasz.
SELECT count(*) AS exact_count FROM myschema.mytable;
Oszacowanie
Niezwykle szybki :
SELECT reltuples AS estimate FROM pg_class where relname = 'mytable';
Zazwyczaj oszacowanie jest bardzo zbliżone. Jak blisko, zależy od tego, czy ANALYZE
lub VACUUM
są wystarczająco uruchomione - gdzie "wystarczająco" jest określone przez poziom aktywności zapisu do twojego stołu.
Bezpieczniejsze oszacowanie
Powyższe pomija możliwość istnienia wielu tabel o tej samej nazwie w jednej bazie danych - w różnych schematach. Aby to uwzględnić:
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';
Rzut na bigint
formatuje real
liczba ładnie, szczególnie dla dużych liczb.
Lepsze oszacowanie
SELECT reltuples::bigint AS estimate
FROM pg_class
WHERE oid = 'myschema.mytable'::regclass;
Szybciej, prościej, bezpieczniej, bardziej elegancko. Zobacz podręcznik na temat typów identyfikatorów obiektów.
Zastąp 'myschema.mytable'::regclass
z to_regclass('myschema.mytable')
w Postgresie 9.4+, aby otrzymać nic zamiast wyjątku dla nieprawidłowych nazw tabel. Zobacz:
- Jak sprawdzić, czy tabela istnieje w danym schemacie
Jeszcze lepsze oszacowanie (przy bardzo niewielkim dodatkowym koszcie)
Możemy zrobić to, co robi planista Postgres. Cytując przykłady szacowania wierszy w instrukcji:
Te liczby są aktualne na dzień ostatniego VACUUM
lub ANALYZE
na stole. Następnie planista pobiera aktualną liczbę stron z tabeli (jest to tania operacja, niewymagająca skanowania tabeli). Jeśli to różni się od relpages
następnie reltuples
jest odpowiednio skalowany, aby uzyskać aktualną szacunkową liczbę wierszy.
Postgres używa estimate_rel_size
zdefiniowane w src/backend/utils/adt/plancat.c
, który obejmuje również narożny przypadek braku danych w pg_class
ponieważ relacja nigdy nie została odkurzona. Możemy zrobić coś podobnego w SQL:
Minimalna forma
SELECT (reltuples / relpages * (pg_relation_size(oid) / 8192))::bigint
FROM pg_class
WHERE oid = 'mytable'::regclass; -- your table here
Bezpieczne i jednoznaczne
SELECT (CASE WHEN c.reltuples < 0 THEN NULL -- never vacuumed
WHEN c.relpages = 0 THEN float8 '0' -- empty table
ELSE c.reltuples / c.relpages END
* (pg_relation_size(c.oid) / pg_catalog.current_setting('block_size')::int)
)::bigint
FROM pg_class c
WHERE c.oid = 'myschema.mytable'::regclass; -- schema-qualified table here
Nie łamie się z pustymi tabelami i tabelami, które nigdy nie widziały VACUUM
lub ANALYZE
. Podręcznik na pg_class
:
Jeśli tabela nigdy nie została jeszcze odkurzona ani przeanalizowana, reltuples
zawiera -1
wskazując, że liczba wierszy jest nieznana.
Jeśli to zapytanie zwraca NULL
, uruchom ANALYZE
lub VACUUM
do stołu i powtórz. (Alternatywnie możesz oszacować szerokość wiersza na podstawie typów kolumn, takich jak Postgres, ale jest to żmudne i podatne na błędy.)
Jeśli to zapytanie zwraca 0
, tabela wydaje się być pusta. Ale ja bym ANALYZE
upewniać się. (A może sprawdź swój autovacuum
ustawienia.)
Zazwyczaj block_size
wynosi 8192. current_setting('block_size')::int
obejmuje rzadkie wyjątki.
Kwalifikacje tabeli i schematu sprawiają, że jest on odporny na każdą search_path
i zakres.
Tak czy inaczej, zapytanie konsekwentnie zajmuje mi <0,1 ms.
Więcej zasobów internetowych:
- Najczęściej zadawane pytania dotyczące Postgres Wiki
- Strony wiki Postgresa dla szacowanej liczby i wydajności count(*)
TABLESAMPLE SYSTEM (n)
w Postgresie 9.5+
SELECT 100 * count(*) AS estimate FROM mytable TABLESAMPLE SYSTEM (1);
Jak skomentował @a_horse, dodana klauzula dla SELECT
polecenie może być przydatne, jeśli statystyki w pg_class
z jakiegoś powodu nie są wystarczająco aktualne. Na przykład:
- Brak
autovacuum
bieganie. - Zaraz po dużym
INSERT
/UPDATE
/DELETE
. TEMPORARY
tabele (które nie są objęteautovacuum
).
To dotyczy tylko losowych n % (1
w przykładzie) wybór bloków i zliczanie w nim wierszy. Większa próbka zwiększa koszty i zmniejsza błąd, Twój wybór. Dokładność zależy od większej liczby czynników:
- Rozkład wielkości wiersza. Jeśli dany blok zawiera szersze niż zwykle wiersze, liczba jest mniejsza niż zwykle itp.
- Martwe krotki lub
FILLFACTOR
zajmują miejsce na blok. W przypadku nierównomiernego rozmieszczenia w tabeli oszacowanie może być nieprawidłowe. - Ogólne błędy zaokrąglania.
Zazwyczaj oszacowanie z pg_class
będzie szybszy i dokładniejszy.
Odpowiedź na aktualne pytanie
Najpierw muszę znać liczbę wierszy w tej tabeli, jeśli suma jest większa niż jakaś predefiniowana stała,
I czy to...
... jest możliwe w momencie, gdy licznik przekroczy moją stałą wartość, zatrzyma liczenie (i nie będzie czekał na zakończenie liczenia, aby poinformować, że liczba wierszy jest większa).
Tak. Możesz użyć podzapytania z LIMIT
:
SELECT count(*) FROM (SELECT 1 FROM token LIMIT 500000) t;
Postgres właściwie przestaje liczyć poza podany limit otrzymujesz dokładny i aktualny licz na maksymalnie n wiersze (500000 w przykładzie) i n Inaczej. Nie tak szybko, jak oszacowanie w pg_class
jednak.