PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Оптимизиране на заявка за преброяване за PostgreSQL

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.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. UTF16 шестнадесетичен към текст

  2. PostgreSQL поддържа ли прозрачно компресиране на таблици (фрагменти)?

  3. Postgresql 11:Грешка при извикване на съхранена процедура - За да извикате процедура, използвайте CALL, Java

  4. Как да настроите отдалечена връзка с PostgreSQL

  5. Как мога да добавя колона, която не позволява нулеви стойности в база данни на Postgresql?