Czy PostgreSQL może używać wartości null w swoich indeksach?

10

Czytałem tę książkę, która tak mówi

Baza danych zakłada, że ​​Indexed_Col IS NOT NULL obejmuje zbyt duży zakres, aby był użyteczny, więc baza danych nie będzie prowadzić do indeksu z tego warunku.

Rozumiem, że książka ma ponad 10 lat, ale okazała się już całkiem przydatna - używając instrukcji zebranych z jej stron przyspieszyłem zapytanie dziesięciokrotnie.

Ponadto, w prowadzeniu EXPLAIN ANALYZEna SELECTzapytania, znalazłem, że żaden z moich indeksy są używane, nawet kiedy przez wszystkich praw, powinny one być.

Zatem moje pytanie brzmi:

Przypuśćmy, że istnieje tabela z kolumną, której definicja kolumny zawiera „NOT NULL” i że istnieje indeks obejmujący tę kolumnę, czy ten indeks mógłby być użyty w zapytaniu tej tabeli, w której kolumny są częścią zapytania?

Lubić:

CREATE TABLE my_table(
a varchar NOT NULL
);

CREATE INDEX ix_my_table ON my_table(a);

SELECT a from my_table;
FuriousFolder
źródło

Odpowiedzi:

9

PostgreSQL z pewnością może używać indeksu dla IS NOT NULL. Nie widzę też żadnych założeń planisty zapytań dotyczących tego warunku.

Jeśli ułamek zerowy dla kolumny ( pg_statistic.stanullfrac) jest wystarczająco niski, aby sugerować, że indeks jest użyteczny selektywnie dla zapytania, PostgreSQL użyje indeksu.

Nie mogę zrozumieć, co próbujesz powiedzieć:

Jeśli jest to poprawne, czy rozumiem, że indeks w kolumnie zdefiniowanej jako „NOT NULL” nie może być używany w zapytaniu korzystającym z tej kolumny?

Z pewnością indeks nie zostanie użyty dla IS NOT NULLwarunku w NOT NULLkolumnie. Zawsze pasowałoby do 100% wierszy, więc seqscan prawie zawsze będzie znacznie szybszy.

PostgreSQL nie użyje indeksu, jeśli indeks nie odfiltruje dużej części wierszy dla zapytania. Jedynym prawdopodobnym wyjątkiem jest prośba o zestaw kolumn objętych jednym indeksem, w kolejności zgodnej z kolejnością indeksu. PostgreSQL może wtedy wykonać skanowanie tylko do indeksu. Np. Jeśli jest włączony indeks, t(a, b, c)a ty:

select a, b FROM t ORDER BY a, b, c;

PostgreSQL może korzystać z Twojego indeksu, nawet jeśli nie są odfiltrowywane żadne wiersze, ponieważ musi on tylko odczytać indeks i może pominąć czytanie sterty, unikać sortowania itp.

Craig Ringer
źródło
Wszystko to dotyczy wersji PG 9.0
eradman
1
I nawet w kolumnie dopuszczającej wartości zerowe zapytanie z warunkiem WHERE column IS NOT NULLmoże nie używać indeksu, ponieważ, jak mówi książka: „obejmuje zbyt duży zakres, aby był użyteczny”. Jeśli 90% wartości nie jest równe null, sekwak będzie prawdopodobnie również szybszy.
ypercubeᵀᴹ
Dokładnie. Może, ale tylko wtedy, gdy duża część tabeli jest pusta. Często w tym przypadku indeks częściowy jest i tak lepszym wyborem.
Craig Ringer
Tak. Próbowałem powiedzieć, że (jak rozumiem) część „obejmuje zbyt duży zakres” odnosi się do indeksu, ale w odniesieniu do konkretnego warunku, a nie indeksu w ogóle.
ypercubeᵀᴹ
2
@FuriousFolder Heh, tutaj jest zbyt wiele negacji. PostgreSQL nie użyje indeksu w NOT NULLkolumnie dla IS NOT NULLzapytania, chyba że ten indeks jest również użyteczny dla innych części WHEREklauzuli, filtrów łączenia itp., Lub jest użyteczny dla uporządkowanego skanowania tylko indeksu. Innymi słowy, całkowicie zignoruje nadmiar IS NOT NULLw NOT NULLkolumnie i dokona wyboru indeksu na podstawie innych szczegółów. (Patrz edycja, ponowne skanowanie tylko w indeksie).
Craig Ringer,
2

Oprócz dokładnej odpowiedzi Craiga chciałem dodać, że na okładce książki, do której się odwołujesz, napisano:

Obejmuje Oracle, DB2 i SQL Server

Dlatego nie ufałbym, że jest to świetne źródło porad w szczególności na temat PostgreSQL. Każdy RDBMS może być zaskakująco inny!

Jestem trochę zdezorientowany co do twojego pierwotnego pytania, ale oto przykład pokazujący, że ta część książki nie jest w 100% poprawna. Aby uniknąć dalszych nieporozumień, oto cały odpowiedni akapit, który można zobaczyć w Google Book Search .

Baza danych zakłada, że ​​Indexed_Col IS NOT NULL obejmuje zbyt duży zakres, aby był użyteczny, więc baza danych nie będzie prowadzić do indeksu z tego warunku. W rzadkich przypadkach posiadanie wartości różnej od wartości zerowej jest tak rzadkie, że korzystne jest skanowanie zakresu indeksu dla wszystkich możliwych wartości niepustych. W takich przypadkach, jeśli uda się ustalić bezpieczną dolną lub górną granicę zakresu wszystkich możliwych wartości, można włączyć skanowanie zakresu z warunkiem, takim jak kolumna_pozycyjna_dodatkowa> -1 lub kolumna_data> TO_DATE ('0001/01/01' , „RRRR / MM / DD”).

Postgres może faktycznie (w poniższym wymyślonym przypadku) użyć indeksu do zaspokojenia IS NOT NULLzapytań bez dodawania bloków skanowania zakresu, jak sugerowano Positive_ID_Column > -1. Zobacz komentarze do pytań Craiga, aby dowiedzieć się, dlaczego Postgres wybiera ten indeks w tym konkretnym przypadku, oraz uwagę na temat używania indeksów częściowych.

CREATE TABLE bar (a int);
INSERT INTO bar (a) SELECT NULL FROM generate_series(1,1000000);
INSERT INTO bar (a) VALUES (1);
CREATE INDEX bar_idx ON bar (a);

EXPLAIN ANALYZE SELECT * FROM bar WHERE a IS NOT NULL;
                                                QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Index Only Scan using bar_idx on bar  (cost=0.42..8.44 rows=1 width=4) (actual time=0.094..0.095 rows=1 loops=1)
   Index Cond: (a IS NOT NULL)
   Heap Fetches: 1
 Total runtime: 0.126 ms
(4 rows)

Nawiasem mówiąc, jest to Postgres 9.3, ale uważam, że wyniki byłyby w przybliżeniu podobne do wersji 9.1, chociaż nie użyłby „Skanowania samego indeksu”.

Edycja: Widzę, że wyjaśniłeś swoje pierwotne pytanie i najwyraźniej zastanawiasz się, dlaczego Postgres nie używa indeksu w prostym przykładzie, takim jak:

CREATE TABLE my_table(
a varchar NOT NULL
);

CREATE INDEX ix_my_table ON my_table(a);

SELECT a from my_table;

Prawdopodobnie dlatego, że nie masz żadnych wierszy w tabeli. Dodaj więc dane testowe i ANALYZE my_table;.

Josh Kupershmidt
źródło
W opisie tej książki (wyróżnienie moje): „Autor Dan Tow przedstawia metodę oszczędzania czasu, którą opracował w celu znalezienia optymalnego planu wykonania - szybko i systematycznie - niezależnie od złożoności używanego SQL lub platformy bazy danych ”. Ponadto, być może przeoczyłeś nr 1 pytania, a mianowicie, że kolumna jest zdefiniowana jako NOT NULL, a nie zapytanie używane IS NOT NULLjako warunek indeksu. To jest w komentarzach, do których się odwołałeś, ale zaktualizuję pytanie, aby je uwzględnić.
FuriousFolder,
Co więcej, sama książka jest niezależna od języka: jedyne części specyficzne dla DMBS dotyczą pokazywania planów zapytań, co Postgres czyni dość prostym :)
FuriousFolder
1
@FuriousFolder kolumna jest zdefiniowana jako NIE NULL, ale ta część (w twoim pytaniu, z książki): „że indeks Indexed_Col NIE JEST NULL ...” odnosi się do warunku gdzie, a nie do definicji kolumny. Chociaż trudno jest być pewnym, ponieważ nie jest to kontekst. Być może powinieneś dołączyć cały (poprzedni) akapit z książki.
ypercubeᵀᴹ
-1

Nie opublikowałeś zapytania ani przykładowych danych. Ale najczęstsze przyczyny, dla których indeksy nie są używane, dotyczą objętości.

Indeksy są jak książka telefoniczna, która tłumaczy kolumnę na lokalizację wiersza. Jeśli szukasz tylko kilku wierszy, warto spojrzeć na każdy wiersz w książce telefonicznej, a następnie na wiersz w głównej tabeli.

Ale w przypadku kilku wierszy taniej jest pomijać książkę telefoniczną i powtarzać wszystkie wiersze w głównej tabeli. Z mojego doświadczenia wynika, że ​​punkt krytyczny wynosi około 100 rzędów.

Andomar
źródło
„Indeksy są jak książka telefoniczna, która tłumaczy kolumnę na lokalizację wiersza. Jeśli szukasz tylko kilku wierszy, warto poszukać każdego wiersza w książce telefonicznej, a następnie wyszukać wiersz w głównej tabeli”. W rzeczywistości indeksy są jak mniejsze książki telefoniczne, które są aktualizowane za każdym razem, gdy aktualizowana jest książka telefoniczna, którą indeksują. Wiesz, że za każdym razem, gdy otworzysz mniejszą książkę telefoniczną, znajdziesz wszystkie informacje, które opisuje jej stan indeksowania. Np Wszyscy ludzie o nazwie „Frank” w tabeli indeksu: CREATE INDEX ix_frank ON people(name) WHERE name ='frank'.
FuriousFolder
Pozwala to indeks tylko do skanowania będzie znacznie znacznie szybciej, ponieważ można przeczytać całą książkę telefoniczną „mniejszy” do pamięci, co nie jest możliwe z wielu milionów wyłożonej tabeli.
FuriousFolder
@FuriousFolder: Opisujesz skanowanie tylko do indeksu. Ale OP twierdzi, że jego indeksy nie są używane, co nie miałoby miejsca, gdyby skanowanie tylko indeksu spełniało zapytanie.
Andomar,
Andomar ... Jestem OP, haha. Moim celem jest właśnie to; aby uzyskać to zapytanie, aby użyć skanowania tylko do indeksu. Osiągnąłem to, ponieważ Craig wyjaśnił, że postgres jest w stanie użyć indeksu na kolumnie, w której definicja kolumny zawiera NOT NULL
FuriousFolder