Wydajnosc count(*) vs count(1) ?

0

Witam,

Czy wydajnosc pomiedzy count(1) and count(*) zostala juz naprawiona w Mysql czy PostgreSQL? Nie moge znalezc jednoznaczniej info. Ale to juz chyba zostalo zrobione, naprawione wieki temu?

2

Nie wiem czy było zepsute, żeby naprawiać :-)

count(*) - * to taki alias, żeby nie pisać wszystkich kolumn, więc jak mówisz 'drogi silniku, potrzebuję żebyś wziął pod uwagę wszystkie kolumny z tabelki', to jest takie info dla bazy, że ma wziąć wszystkie kolumny, a może je wziąć z tabelki albo indeksu, który zawiera wszystkie kolumny.

count(0) - 0 jest wyrażeniem, więc informacja dla bazy, że ta nie musi zupełnie odwoływać się do kolumn w tabeli, a może zrobić to sprytniej (np. full index scan zamiast full table scana).

Jak masz tabelkę np. 1GB i na niej indeks 10MB, to oczywiste, że full scan na indeksie będzie szybszy, bo do oczytania jest tylko 10MB.

Silniki bazodanowe są sprytnie i niektóre potrafią "przerobić" zapytanie, ale czasem i tak użytkownik napisze tak słabe query, że maszyna wymięknie.

0

Fakt, ma to sense co napisales. Kumpel w robocie sprzedal mi takie info, ze jezeli chodzi o wydajnosc to nie ma zadnej roznicy pomiedzy tymi dwoma zapytaniami.

0

A z drugiej storny moze jest tak, ze sql automatyczine przeskakuje na najpptymalniejsza wersje zapytania z wyrazeniem count. Troche dziwne, bo powiedzial to gosc ktory jest naprawde dobry programista. Nie pisze tutaj o jakims mid dev (jak ja np). Tylko seniorze. Ale mozesz i miec racje.

1

Zapytanie rozkładane jest na takie drzewko kroków do wykonania, przy czym takich drzewek (planów wykonania) może być wiele, wybierane jest to o "najniższym koszcie". Dużo zależy od konkretnego optymalizatora zapytań i silnika (jakie operacje potrafi wykonywać), rozkładu danych i kosztu operacji (np. oracle generuje sobie statystyki systemowe, ile czasu zajmuje mu odczyt pojedynczego bloku, ile bloków może max. przeczytać jednym strzałem do macierzy itd.)

Mi nie chce się tego weryfikować, ale jak chcesz się pobawić postgresem, to możesz wygenerować np. tabelkę ze 10M wierszy:

CREATE TABLE foo AS SELECT x as id, lpad('',2000,'*') dummy_data  FROM generate_series(1,10e7) AS gs(x);

Zmierzyć czas wykonania:

  1. select count(*) from foo;
  2. select count(0) from foo;

Powinien polecieć full table scanem, bo nie ma innej możliwości.

Utworzyć indeks:
create index foo_id_idx on foo(id);

Tu dajemy optymalizatorowi możliwość skorzystania z indeksu.

Powinien być używany w zapytaniach typu:
select count(0) from foo;
albo
select count(id) from foo;

Postgres powinien skanować po foo_id_idx ( 10 000 000 * 4 bajty na inta => ~40MB ) - bo w indeksie ma kolumnę id, więc nie ma potrzeby zaglądania do tabelki jak odwołujemy się tylko do id, albo jak w count(wyrazenie) jest wyrazenie, które postgres może sobie wyliczyć bez odwoływania się do kolumn z tabeli.

Tabelka ma jakieś 2004*10 000 000 bajtów => 18-19 GB, więc różnica z użyciem indeksu powinna być wyraźna. Chyba, ze masz turbo szybkie dyski i dużo RAMu :-)

5

Count(0) lub Count(1) to NAJGŁUPSZA rzecz, jaką można było wymyślić. Rozumiem Count(id).
Proponuję potestować Count(0) i Count(*) z LEFT JOINami :)

0

Dlaczego najgłupsza? Wyjaśnij.

3
  1. EXPLAIN ANALYZE, a zobaczysz że nie ma różnicy pomiędzy Count(*) a Count(1)
  2. Coś z niczego: select count(...
0

Oczywistą oczywistością jest, że wyniki tych dwóch funkcji mogą się rożnić. Count(*) zliczy wszystkie wiersze w tabeli, niezależnie czy mamy nulle w kilku czy nawet wszystkich kolumnach.
Count (nazwa_kolumny) zliczy tylko wiersze, w ktorych kolumna wskazana nie jest nullem. Funkcje agregujące pomijają wartości null. Co do wydajności działa tak samo bynajmniej w Oraclu.

0

Postgres (u mnie wersja 11 z dockera).

  1. Budujemy test case
create table foo (
	id int,
	attr1	varchar(200),
	attr2	date
);

insert into foo 
	select t.i,rpad(i::varchar(10),20,'*'),now() from generate_series(1,1e6) as t(i);

create index ix_foo_attr1 on foo(attr1);
  1. Sprawdzamy wersję z count(0) i count (f.*):
postgres=# explain select count(f.*) from foo f where attr1='bar';
                                   QUERY PLAN                                    
---------------------------------------------------------------------------------
 Aggregate  (cost=8.45..8.46 rows=1 width=8)
   ->  Index Scan using ix_foo_attr1 on foo f  (cost=0.42..8.44 rows=1 width=53)
         Index Cond: ((attr1)::text = 'bar'::text)
(3 rows)

postgres=# explain select count(0) from foo f where attr1='bar';
                                     QUERY PLAN                                      
-------------------------------------------------------------------------------------
 Aggregate  (cost=8.45..8.46 rows=1 width=8)
   ->  Index Only Scan using ix_foo_attr1 on foo f  (cost=0.42..8.44 rows=1 width=0)
         Index Cond: (attr1 = 'bar'::text)
(3 rows)

Różnica z pozoru niewielka: "Index Scan" vs "Index Only Scan".

W pierwszym przypadku oprócz skanowania indeksu dojdzie odwołanie do tabeli, w drugim przypadku wystarczy sam indeks, żeby dać odpowiedź.

Teraz jak mamy OLTP z dużą ilością zapytań i mamy np. 3 bloki indeksu do przeczytania (np. header, branch, leaf), to 1 mln zapytań generuje:

  1. 4 mln odczytań bloków (3 mln dla indeksu + 1 mln dla tabelki)
  2. 3 mln odczytań bloków (tylko indeks)

Czy to znacząca różnica i czy ten 1mln ma znaczenie, to już inna sprawa :-)

0

@yarel na tych samych danych, ale z COUNT(*) daje taki wynik:

hauleth=# EXPLAIN ANALYZE select count(*) from foo f where attr1='bar';
                                            QUERY PLAN                                                           
------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=2.65..2.65 rows=1 width=8) (actual time=0.042..0.042 rows=1 loops=1)
   ->  Index Only Scan using ix_foo_attr1 on foo f  (cost=0.42..2.64 rows=1 width=0) (actual time=0.039..0.039 rows=0 loops=1)
         Index Cond: (attr1 = 'bar'::text)
         Heap Fetches: 0
 Planning time: 0.095 ms
 Execution time: 0.076 ms
(6 rows)

Time: 5.110 ms

Więc nie ma różnicy między tym a COUNT(0).

1 użytkowników online, w tym zalogowanych: 0, gości: 1