sql >> Databasteknik >  >> RDS >> PostgreSQL

Optimering av räkningsfråga för PostgreSQL

PostgreSQL stöder faktiskt GIN-index på matriskolumner. Tyvärr verkar det inte vara användbart för NOT ARRAY[...] <@ indexed_col och GIN index är ändå olämpliga för ofta uppdaterade tabeller.

Demo:

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

Tyvärr visar detta att vi som skrivet inte kan använda indexet. Om du inte förnekar villkoret kan det användas, så att du kan söka efter och räkna rader som gör innehålla sökelementet (genom att ta bort NOT ).

Du kan använda indexet för att räkna poster som gör innehålla målvärdet, subtrahera sedan resultatet från en räkning av alla poster. Sedan count Att alla rader i en tabell är ganska långsamt i PostgreSQL (9.1 och äldre) och kräver en sekventiell genomsökning, detta kommer faktiskt att vara långsammare än din nuvarande fråga. Det är möjligt att på 9.2 en index-endast skanning kan användas för att räkna raderna om du har ett b-tree index på id , i så fall kan detta faktiskt vara OK:

SELECT (
  SELECT count(id) FROM arrtable
) - (
  SELECT count(id) FROM arrtable 
  WHERE (ARRAY[1] <@ arrtable.array_column)
);

Det kommer garanterat att prestera sämre än din originalversion för Pg 9.1 och lägre, eftersom ditt original också utöver seqscan kräver det behöver en GIN-indexskanning. Jag har nu testat detta på 9.2 och det verkar använda ett index för räkningen, så det är värt att utforska för 9.2. Med lite mindre triviala dummydata:

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

Observera att ett GIN-index som detta kommer att sakta ner uppdateringarna MYCKET, och är ganska långsamt att skapa i första hand. Det är inte lämpligt för tabeller som uppdateras mycket alls - som ditt bord.

Ännu värre är att frågan som använder detta index tar upp till dubbelt så lång tid som din ursprungliga fråga och i bästa fall hälften så lång på samma datamängd. Det är värst i fall där indexet inte är särskilt selektivt som ARRAY[1] - 4s vs 2s för den ursprungliga frågan. Där indexet är mycket selektivt (dvs:inte många matchningar, som ARRAY[199] ) den körs på cirka 1,2 sekunder jämfört med originalets 3:or. Det här indexet är helt enkelt inte värt att ha för den här frågan.

Lektionen här? Ibland är det rätta svaret bara att göra en sekventiell skanning.

Eftersom det inte fungerar för dina träfffrekvenser, behåll antingen en materialiserad vy med en utlösare som @debenhur föreslår, eller försök att invertera arrayen till en lista över parametrar som posten inte gör. har så att du kan använda ett GiST-index som @maniek föreslår.



  1. SQL Server Integration Services 2008-2005 kompatibilitet

  2. MySQL fulltextsökning i flera tabeller

  3. Importerar csv-fil med null-värden till phpmyadmin

  4. Hitta kolumner som INTE är NULL i PostgreSQL