PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Die Postgresql 9.4-Abfrage wird zunehmend langsamer, wenn Sie TSTZRANGE mit &&beitreten

Ausschlussbeschränkung

Ich schlage vor, dass Sie stattdessen eine Ausschlussbeschränkung verwenden, die viel einfacher, sicherer und schneller ist:

Sie müssen das zusätzliche Modul btree_gist Erste. Siehe Anweisungen und Erläuterungen in dieser verwandten Antwort:

Und Sie müssen "ParentID" angeben in der Tabelle "Bar" überflüssig, was ein geringer Preis sein wird. Tabellendefinitionen könnten so aussehen:

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

Ich habe auch den Datentyp für "Bar"."FooID" geändert von int8 zu int4 . Es verweist auf "Foo".."FooID" , was eine Seriennummer ist , also int4 . Verwenden Sie den übereinstimmenden Typ int4 (oder einfach Integer ) aus mehreren Gründen, einer davon ist die Leistung.

Sie brauchen keinen Trigger mehr (zumindest nicht für diese Aufgabe) und Sie erstellen den Index "Bar_FooID_Timerange_idx" nicht nicht mehr, da es implizit durch die Ausschlussbeschränkung erstellt wird.

Ein btree-Index auf ("ParentID", "FooID") wird aber höchstwahrscheinlich nützlich sein:

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

Verwandte:

Ich habe UNIQUE ("ParentID", "FooID") gewählt und nicht umgekehrt, da es einen anderen Index mit führendem "FooID" gibt in beiden Tabellen:

Abgesehen davon:Ich verwende niemals CaMeL in doppelten Anführungszeichen -Fallbezeichner in Postgres. Ich mache es hier nur, um Ihrem Layout zu entsprechen.

Überflüssige Spalten vermeiden

Wenn Sie "Bar"."ParentID" nicht einfügen können oder wollen redundant gibt es einen weiteren Schurken Weise - unter der Bedingung, dass "Foo".."ParentID" wird nie aktualisiert . Sorgen Sie dafür, zum Beispiel mit einem Trigger.

Sie können einen IMMUTABLE vortäuschen Funktion:

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

Ich habe den Tabellennamen schemaqualifiziert, um sicherzugehen, wobei public angenommen wurde . An Ihr Schema anpassen.

Mehr:

Verwenden Sie es dann in der Ausschlussbeschränkung:

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

Beim Speichern eines redundanten int4 -Spalte, ist die Überprüfung der Einschränkung teurer und die gesamte Lösung hängt von mehr Vorbedingungen ab.

Konflikte behandeln

Sie könnten INSERT umbrechen und AKTUALISIEREN in eine plpgsql-Funktion und fangen mögliche Ausnahmen von der Ausschlussbeschränkung ab (23P01 Exclusion_Violation ), um es irgendwie zu handhaben.

INSERT ...

EXCEPTION
    WHEN exclusion_violation
    THEN  -- handle conflict

Vollständiges Codebeispiel:

Konfliktbehandlung in Postgres 9.5

In Postgres 9.5 Sie können mit INSERT umgehen direkt mit der neuen "UPSERT"-Implementierung. Die Dokumentation:

Allerdings:

Aber Sie können immer noch ON CONFLICT DO NOTHING verwenden , wodurch mögliche exclusion_violation vermieden werden Ausnahmen. Überprüfen Sie einfach, ob tatsächlich Zeilen aktualisiert wurden, was billiger ist:

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

IF NOT FOUND THEN
   -- handle conflict
END IF;

Dieses Beispiel schränkt die Prüfung auf die angegebene Ausschlussbedingung ein. (Ich habe die Einschränkung explizit für diesen Zweck in der obigen Tabellendefinition benannt.) Andere mögliche Ausnahmen werden nicht abgefangen.