Czy kolumny z tablicami indeksów PostgreSQL?

144

W dokumentacji nie mogę znaleźć jednoznacznej odpowiedzi na to pytanie. Jeśli kolumna jest typem tablicowym, czy wszystkie wprowadzone wartości będą indeksowane indywidualnie?

Stworzyłem prostą tabelę z jedną int[]kolumną i umieściłem na niej unikalny indeks. Zauważyłem, że nie mogłem dodać tej samej tablicy liczb int, co prowadzi mnie do przekonania, że ​​indeks jest złożeniem elementów tablicy, a nie indeksem każdego elementu.

INSERT INTO "Test"."Test" VALUES ('{10, 15, 20}');
INSERT INTO "Test"."Test" VALUES ('{10, 20, 30}');

SELECT * FROM "Test"."Test" WHERE 20 = ANY ("Column1");

Czy indeks pomaga w tym zapytaniu?

IamIC
źródło

Odpowiedzi:

181

Tak, możesz indeksować tablicę, ale musisz użyć operatorów tablic i typu indeksu GIN .

Przykład:

    CREATE TABLE "Test"("Column1" int[]);
    INSERT INTO "Test" VALUES ('{10, 15, 20}');
    INSERT INTO "Test" VALUES ('{10, 20, 30}');

    CREATE INDEX idx_test on "Test" USING GIN ("Column1");

    -- To enforce index usage because we have only 2 records for this test... 
    SET enable_seqscan TO off;

    EXPLAIN ANALYZE
    SELECT * FROM "Test" WHERE "Column1" @> ARRAY[20];

Wynik:

Bitmap Heap Scan on "Test"  (cost=4.26..8.27 rows=1 width=32) (actual time=0.014..0.015 rows=2 loops=1)
  Recheck Cond: ("Column1" @> '{20}'::integer[])
  ->  Bitmap Index Scan on idx_test  (cost=0.00..4.26 rows=1 width=0) (actual time=0.009..0.009 rows=2 loops=1)
        Index Cond: ("Column1" @> '{20}'::integer[])
Total runtime: 0.062 ms
Uwaga

wydaje się, że w wielu przypadkach opcja gin__int_ops jest wymagana

create index <index_name> on <table_name> using GIN (<column> gin__int_ops)

Nie widziałem jeszcze przypadku, w którym działałoby to z operatorem && i @> bez opcji gin__int_ops

Frank Heikens
źródło
19
Jak przypuszcza OP, w rzeczywistości nie indeksuje to poszczególnych wartości tablicy, ale zamiast tego indeksuje całą tablicę. Tak więc, chociaż pomoże to zapytaniu, o które chodzi (zobacz plan wyjaśnienia), oznacza to, że nie można (łatwo) utworzyć unikalnych ograniczeń dla poszczególnych wartości tablic. To powiedziawszy, jeśli używasz tablic całkowitych, możesz użyć modułu wkład „intarray” do indeksowania poszczególnych wartości tablic, co w wielu przypadkach może być znacznie szybsze. (W IIRC trwają prace nad tym w zakresie wartości tekstowych, ale współautorzy prawdopodobnie byliby mile widziani, aby pomóc w ich wykończeniu).
xzilla,
15
Prosimy nie używać wielkich liter w identyfikatorach PostgreSQL w przykładach kodu, po prostu dezorientuje to ludzi, którzy nie są zaznajomieni z regułami cytowania / zawijania wielkości liter, szczególnie tych, którzy nie znają PostgreSQL.
intgr
6
Powtórzę tutaj mój komentarz: z mojego doświadczenia wynika, że te indeksy oferują niewielkie lub żadne przyspieszenie, chyba żegin__int_ops są używane dla integer[]kolumn. Zajęło mi lata frustracji i szukania innych rozwiązań, zanim odkryłem tę klasę operacyjną. To cudotwórca z pogranicza.
IamIC
1
@IamIC czy to oznacza, że ​​nie powinienem zawracać sobie głowy indeksowaniem tablicy ciągów? I powinienem indeksować tylko tablice liczb całkowitych?
ryan2johnson9
93

@Tregoreg zadał pytanie w komentarzu do swojej oferowanej nagrody:

Obecne odpowiedzi nie działają. Użycie indeksu GIN w kolumnie typu tablicowego nie zwiększa wydajności operatora ANY (). Czy naprawdę nie ma rozwiązania?

Zaakceptowana odpowiedź @ Franka mówi, że należy używać operatorów tablicowych , co nadal jest poprawne dla Postgres 11. Podręcznik:

... standardowa dystrybucja PostgreSQL zawiera klasę operatora GIN dla tablic, która obsługuje indeksowane zapytania przy użyciu tych operatorów:

<@
@>
=
&&

Pełna lista wbudowanych klas operatorów dla indeksów GIN w standardowej dystrybucji znajduje się tutaj.

W Postgres indeksy są powiązane z operatorami (które są zaimplementowane dla pewnych typów), a nie z samymi typami danych, funkcjami czy czymkolwiek innym. To dziedzictwo oryginalnego projektu Postgres Berkeley i bardzo trudne do zmiany. I ogólnie działa dobrze. Tutaj jest wątek na temat pgsql-bugs z komentarzem Tom'a Lane'a.

Niektóre funkcje PostGis (takie jak ST_DWithin()) wydają się naruszać tę zasadę, ale tak nie jest. Te funkcje są przepisywane wewnętrznie, aby używać odpowiednich operatorów .

Indeksowane wyrażenie musi znajdować się po lewej stronie operatora. W przypadku większości operatorów (w tym wszystkich powyższych ) planista zapytań może to osiągnąć, odwracając operandy, jeśli umieścisz indeksowane wyrażenie po prawej stronie - zakładając, że COMMUTATORzdefiniowano a. ANYKonstrukt może być stosowany w połączeniu z różnych operatorów i nie jest sama w sobie operatora. Gdy jest używany jako constant = ANY (array_expression)tylko indeksy obsługujące =operator na elementach tablicy , kwalifikowałby się i potrzebowalibyśmy komutatora = ANY(). Indeksy GIN są wyłączone.

Postgres nie jest obecnie wystarczająco inteligentny, aby wyprowadzić z niego wyrażenie indeksowalne GIN. Na początek nieconstant = ANY (array_expression) jest całkowicie równoważne z array_expression @> ARRAY[constant]. Operatory tablicowe zwracają błąd, jeśli zaangażowane są jakiekolwiek elementy NULL , podczas gdy ANYkonstrukcja może obsłużyć NULL po obu stronach. Istnieją różne wyniki dla niezgodności typów danych.

Powiązane odpowiedzi:

Poza tym

Podczas pracy z integertablicami ( int4nie int2lub int8) bez NULLwartości (jak sugeruje twój przykład) rozważ dodatkowy moduł intarray, który zapewnia wyspecjalizowane, szybsze operatory i obsługę indeksów. Widzieć:

Jeśli chodzi o UNIQUEograniczenie w twoim pytaniu, które pozostało bez odpowiedzi: jest zaimplementowane z indeksem btree na całej wartości tablicy (tak jak podejrzewasz) i wcale nie pomaga w wyszukiwaniu elementów . Detale:

Erwin Brandstetter
źródło
1
Aaaaaaah, czuję się teraz dość zawstydzony, ale po prostu nie przyszło mi do głowy, że postgres nie użyje indeksu, nawet jeśli teoretycznie jest to możliwe. Może to również dlatego, że mój brak wglądu w postgres, na przykład, że indeksy są związane z operatorami. Dziękuję za poświęcenie czasu na odpowiedź na moje źle postawione pytanie i podzielenie się wiedzą!
Tregoreg,
6
@Tregoreg: Nie wstydź się, to naprawdę nie jest zbyt oczywiste. Pamiętam, że sam byłem zdezorientowany, kiedy po raz pierwszy na to wpadłem. Dodatkowe pytanie i wyjaśnienie powinny być przydatne dla ogółu społeczeństwa.
Erwin Brandstetter
1
Z mojego doświadczenia wynika, że te indeksy zapewniają niewielkie lub żadne przyspieszenie, chyba żegin__int_ops są używane dla integer[]kolumn. Zajęło mi lata frustracji i szukania innych rozwiązań, zanim odkryłem tę klasę operacyjną. To cudotwórca z pogranicza.
IamIC
2
@IamIC: Dodałem wskaźniki do intarray. Wydaje się godne uwagi, jak wskazałeś.
Erwin Brandstetter
W przypadku ANY (array_expression) = constantwyrażeń indeksy GIN działają dobrze?
user10375
37

Teraz można indeksować poszczególne elementy tablicy. Na przykład:

CREATE TABLE test (foo int[]);
INSERT INTO test VALUES ('{1,2,3}');
INSERT INTO test VALUES ('{4,5,6}');
CREATE INDEX test_index on test ((foo[1]));
SET enable_seqscan TO off;

EXPLAIN ANALYZE SELECT * from test WHERE foo[1]=1;
                                                QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Index Scan using test_index on test  (cost=0.00..8.27 rows=1 width=32) (actual   time=0.070..0.071 rows=1 loops=1)
   Index Cond: (foo[1] = 1)
 Total runtime: 0.112 ms
(3 rows)

Działa to przynajmniej na Postgres 9.2.1. Zauważ, że musisz zbudować oddzielny indeks dla każdego indeksu tablicy, w moim przykładzie zindeksowałem tylko pierwszy element.

Ed4
źródło
28
Niech to nie zostanie utracone - takie podejście jest beznadziejne dla tablicy o zmiennej długości, w której chcesz użyć operatora ANY ().
Καrτhικ
24
To naprawdę nie jest zbyt przydatne. Jeśli masz stałą liczbę elementów tablicy, wolisz używać oddzielnych kolumn dla każdego elementu (i zwykłych indeksów btree) zamiast budować droższy indeks wyrażenia dla każdego elementu tablicy. Przechowywanie pojedynczych kolumn jest również znacznie tańsze bez narzutu macierzy.
Erwin Brandstetter