Может ли PostgreSQL индексировать столбцы массива?

Я не могу найти однозначный ответ на этот вопрос в документации. Если столбец является типом массива, будут ли все введенные значения индексироваться индивидуально?

Я создал простую таблицу с одним 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");

помогает ли индекс этому запросу?

3 ответов


Да, вы можете индексировать массив, но вы должны использовать операторы массив и тип Джин-индекса.

пример:

    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
Отмечать

оказывается, что во многих случаях Джин_ _ int _ ops требуется

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

Я еще не видел случая, когда он работал бы с оператором & & и @> без параметров Gin__int_ops


@Tregoreg поднял вопрос в комментарии к его предложил баунти:

я не нашел, что текущие ответы работают. Использование индекса GIN на Array-typed столбец не увеличивает производительность любого() оператор. Неужели нет никакого решения?

@Фрэнк принял ответ говорит, что вы должны использовать операторы массив, которая составляет еще правильно для Postgres 10. инструкции:

стандартное распределение PostgreSQL включает классы операторов GIN для одномерных массивов, которые поддерживают индексированные запросы, использующие эти операторы:

 <@
 @>
 =
 &&

полный список встроенных классов операторов для индексов GIN в стандартном распределении приведен здесь.

В Postgres индексы привязаны к операторам, а не данные типы, функции или что-нибудь еще. Это наследие от оригинального дизайна Беркли Postgres и очень трудно изменить. И, как правило, это работает просто отлично. вот поток на pgsql-ошибки с Томом Лейном, комментирующим это.

индексированное выражение должно быть левый оператора. Для большинства операторов (, включая все вышеперечисленное) планировщик запросов может достичь этого, перевернув операнды, если вы поместите индексированное выражение справа-учитывая, что a COMMUTATOR была определена. The ANY построить может использоваться в сочетании с различными операторами и не является самим оператором. При использовании как constant = ANY (array_expression) только индексы обслуживания = оператор on элементы массива будет квалифицироваться, и нам понадобится коммутатор для = ANY(). Gin-индексов нет.

Postgres в настоящее время недостаточно умен, чтобы получить индексируемый GIN выражение от него. Для начала, constant = ANY (array_expression) is не совсем эквивалентно до array_expression @> ARRAY[constant]. Операторы массива возвращают ошибку, если любой NULL элементов, а ANY construct может иметь дело с NULL с любой стороны. И есть разные результаты для несоответствий типов данных.

соответствующие ответы:

Asides

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

что касается UNIQUE ограничение в вашем вопросе, который остался без ответа: это реализовано с индексом btree на весь массив значение (как вы подозревали) и не помогает в поиске элементов на всех. Подробности:


теперь можно индексировать отдельные элементы массива. Например:

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. Обратите внимание, что вам нужно создать отдельный индекс для каждого индекса массива, в моем примере я индексировал только первый элемент.