web-dev-qa-db-fra.com

PostgreSQL peut-il indexer des colonnes de tableaux?

Je ne trouve pas de réponse définitive à cette question dans la documentation. Si une colonne est un type de tableau, toutes les valeurs entrées seront-elles indexées individuellement?

J'ai créé un tableau simple avec un int[] colonne et mettez un index unique dessus. J'ai remarqué que je ne pouvais pas ajouter le même tableau d'ints, ce qui me porte à croire que l'index est un composite des éléments du tableau, pas un index de chaque élément.

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

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

Est-ce que l'index aide cette requête?

124
IamIC

Oui, vous pouvez indexer un tableau, mais vous devez utiliser les opérateurs de tablea et le type d'index GIN .

Exemple:

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

Résultat:

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

il semble que dans de nombreux cas, l'option gin__int_ops est requise

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

Je n'ai pas encore vu de cas où cela fonctionnerait avec l'opérateur && et @> sans les options gin__int_ops

151
Frank Heikens

@Tregoreg a soulevé un question dans le commentaire à sa prime offerte:

Je n'ai pas trouvé les réponses actuelles qui fonctionnent. L'utilisation de l'index GIN sur une colonne de type tableau n'augmente pas les performances de l'opérateur ANY (). N'y a-t-il vraiment pas de solution?

@ Frank est accepté réponse vous dit d'utiliser opérateurs de tableau , ce qui est toujours correct pour Postgres 11. The manual:

... la distribution standard de PostgreSQL inclut une classe d'opérateurs GIN pour les tableaux, qui prend en charge les requêtes indexées à l'aide de ces opérateurs:

<@
@>
=
&&

La liste complète des classes d'opérateurs intégrées pour les index GIN dans la distribution standard est ici.

Dans Postgres , les index sont liés à des opérateurs (qui sont implémentés pour certains types), pas à des types de données ni à des fonctions ou quoi que ce soit d'autre. C’est un héritage du design original de Postgres à Berkeley et très difficile à changer maintenant. Et cela fonctionne généralement très bien. Voici un fil sur pgsql-bugs avec Tom Lane en commentant.

Certains PostGis fonctions (comme ST_DWithin() ) semblent violer ce principe, mais ce n'est pas le cas. Ces fonctions sont réécrites en interne pour utiliser les opérateurs opérateurs respectifs.

L'expression indexée doit être à l'opérateur left. Pour la plupart des opérateurs ( y compris tout ce qui précède), le planificateur de requêtes peut y parvenir en retournant des opérandes si vous placez l'expression indexée à droite - étant donné qu'un COMMUTATOR a été défini. Le ANY construct peut être utilisé en combinaison avec différents opérateurs et n’est pas un opérateur lui-même. Lorsqu'il est utilisé en tant que constant = ANY (array_expression), seuls les index prenant en charge l'opérateur = Sur éléments de tableau seraient admissibles et nous aurions besoin d'un commutateur pour = ANY(). Les index GIN sont sortis.

Actuellement, Postgres n'est pas assez intelligent pour en tirer une expression indexable GIN. Pour commencer, constant = ANY (array_expression) n'est pas totalement équivalent à array_expression @> ARRAY[constant]. Les opérateurs de tableaux renvoient une erreur si des éléments NULL éléments sont impliqués, alors que la construction ANY peut traiter NULL de chaque côté. Et il existe différents résultats pour les disparités de type de données.

Réponses associées:

À part

Tout en travaillant avec integer tableaux (int4, Pas int2 Ou int8 ) sans NULL valeurs (comme votre exemple l’implique) considérons le module supplémentaire intarray , qui fournit aux opérateurs spécialisés et plus rapides et au support des index. Voir:

En ce qui concerne la contrainte UNIQUE de votre question qui est restée sans réponse: elle est implémentée avec un index btree sur la valeur tableau entier (comme vous le supposiez) et ne facilite pas la recherche de éléments du tout. Détails:

79
Erwin Brandstetter

Il est maintenant possible d'indexer les différents éléments du tableau. Par exemple:

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)

Cela fonctionne au moins sur Postgres 9.2.1. Notez que vous devez créer un index distinct pour chaque index de tableau. Dans mon exemple, je n'ai indexé que le premier élément.

34
Ed4