Indeksowanie stanu częściowego indeksu w Postgres

Sep 05 2020

Próbuję zrozumieć, w jaki sposób częściowe indeksy Postgres są przechowywane w Postgres. Załóżmy, że utworzę taki indeks

CREATE INDEX orders_unbilled_index ON orders (order_nr)
WHERE billed is not true

w celu szybkiego uruchomienia zapytania, takiego jak

SELECT *
FROM orders
WHERE billed is not true AND order_nr > 1000000

Postgres oczywiście przechowuje indeks order_nroparty na podzbiorze orderstabeli, zgodnie z definicją wyrażenia warunkowego billed is not true. Mam jednak kilka pytań związanych z tym:

  1. Czy Postgres przechowuje inny indeks wewnętrznie, billed is not trueaby szybko znaleźć wiersze powiązane z częściowym indeksem?
  2. Jeśli tak nie jest (1), czy spowodowałoby to przyspieszenie powyższego zapytania, gdybym utworzył oddzielny indeks na billed is not true? (zakładając dużą tabelę i kilka wierszy z billed is true)

EDYCJA: Moje przykładowe zapytanie oparte na dokumentacji nie jest najlepsze ze względu na to, że indeksy logiczne są rzadko używane , ale proszę rozważyć moje pytania w kontekście dowolnego wyrażenia warunkowego.

Odpowiedzi

2 LaurenzAlbe Sep 06 2020 at 12:08

Indeks b-drzewa można traktować jako uporządkowaną listę pozycji indeksu, z których każdy zawiera wskaźnik do wiersza w tabeli.

W indeksie częściowym lista jest po prostu mniejsza: istnieją tylko wpisy indeksu dla wierszy, które spełniają warunek.

Jeśli w WHEREklauzuli znajduje się warunek indeksu , PostgreSQL wie, że może użyć indeksu i nie musi sprawdzać warunku indeksu, ponieważ zostanie spełniony automatycznie.

Więc:

  1. Nie, każdy wiersz znaleziony za pośrednictwem indeksu automatycznie spełni warunek indeksu, więc użycie indeksu wystarczy, aby upewnić się, że jest on spełniony.

  2. Nie, indeks w kolumnie logicznej nie będzie używany, ponieważ nie byłby tańszy niż ten indeks częściowy, a indeks częściowy może być również użyty do sprawdzenia warunku order_nr.

    W rzeczywistości jest odwrotnie: indeks częściowy może być z powodzeniem używany do zapytań, które mają tylko booleankolumnę w WHEREwarunku, jeśli jest wystarczająco mało wierszy, które spełniają warunek.

TimBiegeleisen Sep 06 2020 at 04:39

Rozumiem, że Postgres po prostu zbuduje indeks, który będzie mógł być używany tylko do wyszukiwania rekordów, które billednie są prawdziwe. Oznacza to, że wynikowe B-drzewo byłoby indeksowane przez order_nr, ale łączyłoby się z oryginalną tabelą tylko wtedy, gdy billedbyłaby fałszywa.

Jeśli będziesz dalej czytać dokumentację , zaraz po tym, co zacytowałeś, jako przykład znajdziesz następujące zapytanie:

SELECT * FROM orders WHERE billed is not true AND amount > 5000.00;

Jest tak, że Postgres może nawet zdecydować się na użycie indeksu, który zdefiniowałeś w powyższym zapytaniu. Może użyć Twojego indeksu, aby spełnić to zapytanie, skanując cały indeks. Jeśli istnieje stosunkowo niewielka liczba zamówień, które nie zostały jeszcze zafakturowane, skanowanie indeksu order_nrnadal może być lepsze niż wykonanie pełnego skanowania tabeli.

Tak więc odpowiedź na twoje pytanie nr 1 brzmi: nie, nie ma oddzielnego indeksu dla billed, ale raczej indeks włączony order_nrmoże być używany tylko dla rekordów, dla których billedustawiono wartość fałsz. W przypadku # 2 tak, można użyć drugiego indeksu na, billed is not truezakładając, że kilka rekordów nie jest rozliczonych. Jednak nawet Twój aktualny indeks może być używany w takiej postaci, w jakiej jest.