Чи може PostgreSQL індексувати стовпці?


144

Я не можу знайти однозначної відповіді на це питання в документації. Якщо стовпець - це тип масиву, чи всі введені значення будуть індексуватися індивідуально?

Я створив просту таблицю з одним int[]стовпцем і поставив на ній унікальний індекс. Я помітив, що не можу додати той самий масив ints, що змушує мене вважати, що індекс - це складова елементів масиву, а не індекс кожного елемента.

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

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

Чи допомагає індекс у цьому запиті?


Чи можливо використовувати тип даних jsonbта використовувати індекси? postgresql.org/docs/9.5/static/functions-json.html та postgresql.org/docs/9.5/static/datatype-json.html#JSON-INDEXING
користувач3791372

Відповіді:


181

Так, ви можете проіндексувати масив, але вам доведеться використовувати оператори масиву та тип індексу GIN .

Приклад:

    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];

Результат:

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
Примітка

виявляється, що у багатьох випадках потрібен варіант gin__int_ops

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

Я ще не бачив жодного випадку, коли він би працював з оператором && і @> без параметрів gin__int_ops


19
Як вважає ОП, це фактично не індексує значення окремих масивів, а натомість індексує весь масив. Отже, хоча це допоможе запиту, про який йдеться (див. План пояснення), це означає, що ви не можете створити унікальні обмеження (легко) для окремих значень масиву. Однак, якщо ви використовуєте цілі масиви, ви можете використовувати модуль contrib "intarray", щоб індексувати окремі значення масиву, що у багатьох випадках може бути набагато швидшим. (IIRC в цьому напрямку робиться певна робота над текстовими значеннями, але, мабуть, вкладачі можуть запросити її допомогти).
xzilla

15
Будь ласка, не використовуйте великі літери в ідентифікаторах PostgreSQL у прикладах коду, це просто збиває з пантелику людей, які не знайомі з правилами складання котирувань / регістрів, особливо людей, які не знайомі з PostgreSQL.
intgr

6
Щоб повторити тут свій коментар: з мого досвіду, ці індекси пропонують майже не швидко прискорити, якщо вони gin__int_ops не використовуються для integer[]стовпців. Мені знадобилися роки розчарування і пошуку інших рішень, поки я не виявив цей клас. Це прикордонний чудотворець.
IamIC

1
@IamIC чи це означає, що я не повинен перейматися індексуванням масиву рядків? І я повинен індексувати лише цілі масиви?
ryan2johnson9

93

@Tregoreg поставив запитання в коментарі до запропонованої йому винагороди:

Я не знайшов діючих відповідей. Використання індексу GIN на стовпчику масиву не збільшує продуктивність оператора ANY (). Невже немає рішення?

@ Прийнята відповідь Франка говорить про використання операторів масиву , що все ще правильно для Postgres 11. Посібник:

... стандартний розподіл PostgreSQL включає клас операторів GIN для масивів, який підтримує індексовані запити за допомогою цих операторів:

<@
@>
=
&&

Повний список вбудованих операторських класів для GIN-індексів у стандартній дистрибуції тут.

У Postgres індекси пов'язані з операторами (які реалізовані для певних типів), а не типами даних або функціями чи чим-небудь іншим. Це спадщина оригінального дизайну Bergley Postgres і зараз дуже важко змінити. І це, як правило, працює просто чудово. Ось нитка про pgsql-помилок, коментуючи це, коментує Том Лейн.

Деякі функції PostGis (наприклад,ST_DWithin() ), здається, порушують цю головну, але це не так. Ці функції переписані внутрішньо для використання відповідних операторів .

Індексований вираз повинен бути зліва від оператора. Для більшості операторів ( включаючи всі перераховані вище ) планувальник запитів може досягти цього, перегортаючи операнди, якщо розмістити індексований вираз праворуч - враховуючи, що а COMMUTATORбуло визначено. ANYКонструкція може бути використана в комбінації з різними операторами і не є сам оператор. При використанні в якості constant = ANY (array_expression)лише індексів, що підтримують =оператора в елементах масиву, буде кваліфіковано, і нам знадобиться комутатор = ANY(). Індекси GIN вичерпані.

На даний момент Postgres недостатньо розумний, щоб отримати з нього вираження, що індексується GIN. Під - перше, constant = ANY (array_expression)це в повному обсязі еквівалентні з array_expression @> ARRAY[constant]. Оператори масиву повертають помилку, якщо вона є NULL задіяні елементи , тоді як ANYконструкція може мати справу з NULL з будь-якої сторони. І є різні результати для невідповідностей типів даних.

Відповідні відповіді:

Убік

Працюючи з integerмасивами ( int4не int2або int8) без NULLзначень (як випливає з вашого прикладу), врахуйте додатковий модуль intarray, який забезпечує спеціалізовані, швидші оператори та підтримку індексів. Побачити:

Що стосується UNIQUEобмеження у вашому запитанні, яке не залишилося без відповіді: це реалізовано з btree індексом для всього значення масиву (як ви підозрювали) і зовсім не допомагає шукати елементи . Деталі:


1
Ааааааах, відчуваючи себе зараз зовсім соромно, але мені просто не спадало на думку, що постгреси не використовуватимуть індекс, навіть якщо теоретично це можливо. Можливо, це також тому, що моя відсутність розуміння постгресів, таких як індекси, прив'язані до операторів. Дякую, що знайшли час, щоб відповісти на моє неправильне питання та поділитися своїми знаннями
Трегорег

6
@Tregoreg: Не соромтеся, це справді не надто очевидно. Я пам’ятаю, як мене це бентежило, коли я вперше наткнувся на це. Додане запитання та уточнення мають бути дуже корисними для широкої громадськості.
Ервін Брандстеттер

1
З мого досвіду, ці індекси пропонують майже не швидкість прискорення, якщо їх gin__int_ops не використовувати для integer[]стовпців. Мені знадобилися роки розчарування і пошуку інших рішень, поки я не виявив цей клас. Це прикордонний чудотворець.
IamIC

2
@IamIC: Я додав покажчики на intarray. Здається, примітно, як ви вказали.
Ервін Брандстеттер

Для ANY (array_expression) = constantвиразів GIN-індекси спрацьовують добре?
користувач10375

37

Тепер можна індексувати окремі елементи масиву. Наприклад:

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)

Це працює щонайменше за Postgres 9.2.1. Зауважте, що вам потрібно створити окремий індекс для кожного індексу масиву, у моєму прикладі я індексував лише перший елемент.


28
Нехай це не буде втрачено - такий підхід є безнадійним для масиву змінної довжини, де потрібно використовувати оператор ANY ().
Καrτhικ

24
Це справді не дуже корисно. Якщо у вас є фіксована кількість елементів масиву, ви краще використовувати окремі стовпці для кожного елемента (та прості індекси btree), а не будувати дорожчий індекс вираження для кожного елемента масиву. Зберігання окремих стовпців набагато дешевше, без накладних масивів.
Ервін Брандштетер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.