Szybki sposób na sprawdzenie liczby wierszy tabeli w PostgreSQL

Muszę znać liczbę wierszy w tabeli, aby obliczyć procent. Jeśli całkowita liczba jest większa niż jakaś predefiniowana stała, użyję stałej wartości. W przeciwnym razie użyję rzeczywistej liczby wierszy.

Mogę użyć SELECT count(*) FROM table. Ale jeśli moja stała wartość jest 500,000 i mam 5,000,000,000 wiersze w mojej tabeli liczenie wszystkich wierszy zmarnuje dużo czasu.

Czy można przestać liczyć, gdy tylko moja stała wartość zostanie przekroczona?

Potrzebuję dokładna liczba wierszy tylko tak długo, jak jest poniżej podanego limitu. W przeciwnym razie, jeśli liczba jest powyżej limitu, używam wartości granicznej zamiast i chcę odpowiedzi tak szybko, jak to możliwe.

Coś takiego:

SELECT text,count(*), percentual_calculus()  
FROM token  
GROUP BY text  
ORDER BY count DESC;
Author: Erwin Brandstetter, 2011-10-30

7 answers

Liczenie wierszy w dużych tabel jest znane jako wolne w PostgreSQL. Aby uzyskać dokładną liczbę, musi wykonać pełną liczbę wierszy ze względu na naturę MVCC. Jest sposób, aby przyspieszyć to dramatycznie jeśli liczba Nie , a nie musi być dokładnie tak, jak wydaje się być w Twoim przypadku.

Zamiast uzyskać dokładny count ( slow z dużymi tabelami):

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

Dostajesz dokładne oszacowanie w ten sposób (niezwykle szybki):

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

Jak blisko jest oszacowanie zależy od tego, czy biegniesz ANALYZE wystarczy. Zwykle jest bardzo blisko.
Zobacz PostgreSQL Wiki FAQ .
Lub dedykowana strona wiki dla wydajności count (*) .

Jeszcze lepiej

Artykuł w PostgreSQL Wiki jest był trochę niechlujny . Zignorował możliwość istnienia wielu tabel o tej samej nazwie w jednej bazie danych - w różnych Schematy. Aby to wyjaśnić:

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'

Or better still

SELECT reltuples::bigint AS estimate
FROM   pg_class
WHERE  oid = 'myschema.mytable'::regclass;
Szybciej, łatwiej, bezpieczniej, bardziej elegancko. Zobacz instrukcję dotyczącą typów identyfikatorów obiektów .

Użyj to_regclass('myschema.mytable') W Postgres 9.4+, aby uniknąć wyjątków dla nieprawidłowych nazw tabel:


TABLESAMPLE SYSTEM (n) W Postgres 9.5 +

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

Jak @ a_horse skomentował , Ostatnio dodane klauzula dla polecenia SELECT może być przydatna, jeśli statystyki w pg_class nie są wystarczająco aktualne z jakiegoś powodu. Na przykład:

  • Nie autovacuum bieganie.
  • bezpośrednio po dużym INSERT lub DELETE.
  • TEMPORARY tabele (które nie są objęte autovacuum).

To wygląda tylko na losowy n % (1 w przykładzie) zaznaczenie bloków i zliczenie wierszy w nim. Większa próbka zwiększa koszt i zmniejsza błąd, twój wybór. Dokładność zależy od więcej czynników:

  • rozkład wielkości wiersza. Jeśli dany blok ma szersze niż zwykle wiersze, liczba jest niższa niż zwykle itd.
  • martwe krotki lub FILLFACTOR zajmują miejsce na blok. Jeśli nierównomiernie rozłożone w tabeli, oszacowanie może być wyłączone.
  • ogólne błędy zaokrąglania.

W większości przypadków oszacowanie z pg_class będzie szybsze i dokładniejsze.

Odpowiedź na rzeczywiste pytanie

Po pierwsze, muszę wiedzieć, liczba wierszy w tej tabeli, jeżeli suma liczba jest większa niż jakaś predefiniowana stała,

I czy to ...

... jest możliwe w momencie, gdy licznik przejdzie moją stałą wartość, będzie zatrzymaj liczenie (i nie czekaj, aby zakończyć liczenie, aby poinformować liczba wierszy jest większa).

Tak. możesz użyć subquery z LIMIT:

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

Postgres faktycznie przestaje liczyć poza podaną granicę, otrzymujesz dokładny i aktualny Licznik do N wierszy (500000 w przykładzie), a N w przeciwnym razie. Jednak nie tak szybko jak szacunki w pg_class.

 149
Author: Erwin Brandstetter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:03:02

Zrobiłem to raz w aplikacji postgres, uruchamiając:

EXPLAIN SELECT * FROM foo;

Następnie zbadanie wyjścia za pomocą regex lub podobnej logiki. Dla prostego wyboru*, pierwsza linia wyjścia powinna wyglądać mniej więcej tak:

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

Możesz użyć wartości rows=(\d+) jako przybliżonego oszacowania liczby wierszy, które zostaną zwrócone, a następnie wykonać rzeczywistą wartość SELECT COUNT(*), Jeśli oszacowanie jest, powiedzmy, mniejsze niż 1,5 x próg (lub jakąkolwiek liczbę uznasz za sensowną dla Twojej aplikacji).

W zależności od złożoność zapytania, liczba ta może stać się coraz mniej dokładna. W moim zgłoszeniu, jak dodaliśmy łączniki i złożone warunki, stało się tak niedokładne, że było całkowicie bezwartościowe, nawet wiedząc, jak w mocy 100 ile rzędów byśmy wrócili, więc musieliśmy porzucić tę strategię.

Ale jeśli Twoje zapytanie jest na tyle proste, że Pg może przewidzieć w rozsądnym marginesie błędu, ile wierszy zwróci, może to zadziałać dla Ciebie.

 8
Author: Flimzy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-30 04:19:02

W Oracle możesz użyć rownum, aby ograniczyć liczbę zwracanych wierszy. Domyślam się, że podobna konstrukcja istnieje również w innych SQL. Tak więc w podanym przykładzie możesz ograniczyć liczbę zwróconych wierszy do 500001 i zastosować count(*) następnie:

SELECT (case when cnt > 500000 then 500000 else cnt end) myCnt
FROM (SELECT count(*) cnt FROM table WHERE rownum<=500001)
 1
Author: Ritesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-30 15:59:25

Możesz uzyskać liczbę za pomocą poniższego zapytania (bez * lub nazw kolumn).

select from table_name;
 1
Author: Vikram S,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-17 09:53:43

Jak szeroka jest kolumna tekstowa?

Z grupą by nie ma wiele można zrobić, aby uniknąć skanowania danych (przynajmniej skanowania indeksów).

Polecam:

  1. Jeśli to możliwe, zmiana schematu w celu usunięcia duplikacji danych tekstowych. W ten sposób liczenie nastąpi na wąskim polu klucza obcego w tabeli 'many'.

  2. Alternatywnie, utworzenie Wygenerowanej kolumny z Hashem tekstu, a następnie pogrupowanie według kolumny hash. Ponownie, ma to na celu zmniejszenie obciążenia pracą (skanowanie przez indeks wąskiej Kolumny)

Edit:

Twoje oryginalne pytanie nie pasowało do Twojej edycji. Nie jestem pewien, czy wiesz, że licznik, gdy jest używany z grupą by, zwróci liczbę elementów na grupę, a nie liczbę elementów w całej tabeli.

 0
Author: Chris Bednarski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-30 05:28:20

Dla SQL Server (2005 lub nowszy) szybka i niezawodna metoda to:

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

Szczegóły dotyczące sys. dm_db_partition_stats są wyjaśnione w MSDN

Zapytanie dodaje wiersze ze wszystkich części (ewentualnie) podzielonej tabeli.

Index_id = 0 jest tabelą nieuporządkowaną (sterta), a index_id=1 jest tabelą uporządkowaną (indeks klastrowy)

Jeszcze szybsze (ale zawodne) metody są szczegółowe tutaj.

 0
Author: DrKoch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-29 08:45:56

Odniesienie zaczerpnięte z tego bloga.

Możesz użyć poniższego zapytania, aby znaleźć liczbę wierszy.

Using pg_class:

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

Użycie pg_stat_user_tables:

SELECT 
    schemaname
    ,relname
    ,n_live_tup AS EstimatedCount 
FROM pg_stat_user_tables 
ORDER BY n_live_tup DESC;
 0
Author: Anvesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-13 07:58:00