PostgreSQL всъщност поддържа GIN индекси на колони от масиви. За съжаление, изглежда, че не може да се използва за NOT ARRAY[...] <@ indexed_col
и GIN
индексите така или иначе са неподходящи за често актуализирани таблици.
Демо:
CREATE TABLE arrtable (id integer primary key, array_column integer[]);
INSERT INTO arrtable(1, ARRAY[1,2,3,4]);
CREATE INDEX arrtable_arraycolumn_gin_arr_idx
ON arrtable USING GIN(array_column);
-- Use the following *only* for testing whether Pg can use an index
-- Do not use it in production.
SET enable_seqscan = off;
explain (buffers, analyze) select count(id)
from arrtable
where not (ARRAY[1] <@ arrtable.array_column);
За съжаление, това показва, че както е написано, не можем да използваме индекса. Ако не отхвърлите условието, то може да се използва, така че можете да търсите и преброявате редове, които правят съдържа елемента за търсене (чрез премахване на NOT
).
Можете да използвате индекса, за да преброите записи, които правят съдържа целевата стойност, след което извадете резултата от броя на всички записи. Тъй като count
Прехвърлянето на всички редове в таблица е доста бавно в PostgreSQL (9.1 и по-стари) и изисква последователно сканиране, което всъщност ще бъде по-бавно от текущата ви заявка. Възможно е на 9.2 сканиране само за индекс да може да се използва за преброяване на редовете, ако имате индекс на b-дърво на id
, в който случай това всъщност може да е ОК:
SELECT (
SELECT count(id) FROM arrtable
) - (
SELECT count(id) FROM arrtable
WHERE (ARRAY[1] <@ arrtable.array_column)
);
Гарантирано е, че ще работи по-лошо от вашата оригинална версия за Pg 9.1 и по-ниски, защото в допълнение към seqscan вашият оригинал го изисква също се нуждае от сканиране на GIN индекс. Сега тествах това на 9.2 и изглежда, че използва индекс за броя, така че си струва да проучите за 9.2. С някои по-малко тривиални фиктивни данни:
drop index arrtable_arraycolumn_gin_arr_idx ;
truncate table arrtable;
insert into arrtable (id, array_column)
select s, ARRAY[1,2,s,s*2,s*3,s/2,s/4] FROM generate_series(1,1000000) s;
CREATE INDEX arrtable_arraycolumn_gin_arr_idx
ON arrtable USING GIN(array_column);
Имайте предвид, че GIN индекс като този ще забави актуализациите МНОГО и е доста бавен за създаване на първо място. Не е подходящ за таблици, които се актуализират много - като вашата таблица.
Още по-лошо, заявката, използваща този индекс, отнема до два пъти повече време от първоначалната ви заявка и в най-добрия случай наполовина по-малко върху същия набор от данни. Най-лошо е за случаите, когато индексът не е много селективен като ARRAY[1]
- 4s срещу 2s за оригиналната заявка. Където индексът е силно избирателен (т.е.:няма много съвпадения, като ARRAY[199]
) работи за около 1,2 секунди срещу 3s на оригинала. Този индекс просто не си струва да имате за тази заявка.
Урокът тук? Понякога правилният отговор е просто да направите последователно сканиране.
Тъй като това няма да свърши работа за вашите проценти на попадение, или поддържайте материализиран изглед с тригер, както предлага @debenhur, или опитайте да обърнете масива, за да бъде списък с параметри, които записът не имате, за да можете да използвате GiST индекс, както предлага @maniek.