PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

La requête Postgresql 9.4 devient progressivement plus lente lors de la jonction de TSTZRANGE avec &&

Contrainte d'exclusion

Je vous suggère plutôt d'utiliser une contrainte d'exclusion, qui est beaucoup plus simple, plus sûre et plus rapide :

Vous devez installer le module supplémentaire btree_gist première. Voir les instructions et l'explication dans cette réponse connexe :

Et vous devez inclure "ParentID" dans le tableau "Bar" de manière redondante, ce qui sera un petit prix à payer. Les définitions de tableau pourraient ressembler à ceci :

CREATE TABLE "Foo" (
   "FooID"    serial PRIMARY KEY
   "ParentID" int4 NOT NULL REFERENCES "Parent"
   "Details1" varchar
   CONSTRAINT foo_parent_foo_uni UNIQUE ("ParentID", "FooID")  -- required for FK
);

CREATE TABLE "Bar" (
   "ParentID"  int4 NOT NULL,
   "FooID"     int4 NOT NULL REFERENCES "Foo" ("FooID"),
   "Timerange" tstzrange NOT NULL,
   "Detail1"   varchar,
   "Detail2"   varchar,
   CONSTRAINT "Bar_pkey" PRIMARY KEY ("FooID", "Timerange"),
   CONSTRAINT bar_foo_fk
      FOREIGN KEY ("ParentID", "FooID") REFERENCES "Foo" ("ParentID", "FooID"),
   CONSTRAINT bar_parent_timerange_excl
      EXCLUDE USING gist ("ParentID" WITH =, "Timerange" WITH &&)
);

J'ai également changé le type de données pour "Bar"."FooID" de int8 à int4 . Il fait référence à "Foo"."FooID" , qui est un serial , c'est-à-dire int4 . Utilisez le type correspondant int4 (ou simplement integer ) pour plusieurs raisons, dont la performance.

Vous n'avez plus besoin d'un déclencheur (du moins pas pour cette tâche), et vous ne créez pas l'index "Bar_FooID_Timerange_idx" plus, puisqu'il est créé implicitement par la contrainte d'exclusion.

Un index btree sur ("ParentID", "FooID") sera très probablement utile, cependant :

CREATE INDEX bar_parentid_fooid_idx ON "Bar" ("ParentID", "FooID");

Connexe :

J'ai choisi UNIQUE ("ParentID", "FooID") et non l'inverse pour une raison, car il existe un autre index avec "FooID" en tête dans l'une ou l'autre des tables :

A part :Je n'utilise jamais CaMeL entre guillemets doubles -identifiants de cas dans Postgres. Je ne le fais ici que pour me conformer à votre mise en page.

Éviter les colonnes redondantes

Si vous ne pouvez pas ou ne voulez pas inclure "Bar"."ParentID" de manière redondante, il y a un autre voyou chemin - à condition que "Foo"."ParentID" n'est jamais mis à jour . Assurez-vous de cela, avec un déclencheur par exemple.

Vous pouvez simuler un IMMUTABLE fonction :

CREATE OR REPLACE FUNCTION f_parent_of_foo(int)
  RETURNS int AS
'SELECT "ParentID" FROM public."Foo" WHERE "FooID" = $1'
  LANGUAGE sql IMMUTABLE;

J'ai qualifié de schéma le nom de la table pour m'en assurer, en supposant que public . Adaptez-vous à votre schéma.

Plus :

Utilisez-le ensuite dans la contrainte d'exclusion :

   CONSTRAINT bar_parent_timerange_excl
      EXCLUDE USING gist (f_parent_of_foo("FooID") WITH =, "Timerange" WITH &&)

Tout en sauvegardant un int4 redondant colonne, la contrainte sera plus coûteuse à vérifier et toute la solution dépend de plus de conditions préalables.

Gérer les conflits

Vous pouvez envelopper INSERT et UPDATE dans une fonction plpgsql et piéger les éventuelles exceptions à la contrainte d'exclusion (23P01 exclusion_violation ) pour le gérer d'une manière ou d'une autre.

INSERT ...

EXCEPTION
    WHEN exclusion_violation
    THEN  -- handle conflict

Exemple de code complet :

Gérer les conflits dans Postgres 9.5

Dans Postgres 9.5 vous pouvez gérer INSERT directement avec la nouvelle implémentation "UPSERT". La documentation :

Cependant :

Mais vous pouvez toujours utiliser ON CONFLICT DO NOTHING , évitant ainsi une éventuelle exclusion_violation exceptions. Vérifiez simplement si des lignes ont bien été mises à jour, ce qui revient moins cher :

INSERT ... 
ON CONFLICT ON CONSTRAINT bar_parent_timerange_excl DO NOTHING;

IF NOT FOUND THEN
   -- handle conflict
END IF;

Cet exemple limite la vérification à la contrainte d'exclusion donnée. (J'ai nommé la contrainte explicitement à cette fin dans la définition du tableau ci-dessus.) Les autres exceptions possibles ne sont pas interceptées.