sql >> Databasteknik >  >> RDS >> PostgreSQL

Postgresql 9.4-frågan blir gradvis långsammare när du går med i TSTZRANGE med &&

Uteslutningsbegränsning

Jag föreslår att du istället använder en uteslutningsbegränsning, som är mycket enklare, säkrare och snabbare:

Du måste installera tilläggsmodulen btree_gist först. Se instruktioner och förklaring i detta relaterade svar:

Och du måste inkludera "ParentID" i tabellen "Bar" överflödigt, vilket kommer att vara ett litet pris att betala. Tabelldefinitioner kan se ut så här:

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

Jag ändrade också datatypen för "Bar"."FooID" från int8 till int4 . Den refererar till "Foo"."FooID" , som är en serie , dvs. int4 . Använd matchningstypen int4 (eller bara heltal ) av flera skäl, en av dem är prestanda.

Du behöver ingen utlösare längre (åtminstone inte för den här uppgiften), och du skapar inte indexet "Bar_FooID_Timerange_idx" längre, eftersom det skapas implicit av uteslutningsrestriktionen.

Ett btree-index på ("ParentID", "FooID") kommer dock med största sannolikhet att vara användbart:

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

Relaterat:

Jag valde UNIQUE ("Förälder-ID", "FooID") och inte tvärtom av en anledning, eftersom det finns ett annat index med ledande "FooID" i endera tabellen:

Bortsett från:Jag använder aldrig dubbla citerade CaMeL -caseidentifierare i Postgres. Jag gör det bara här för att följa din layout.

Undvik överflödig kolumn

Om du inte kan eller vill inkludera "Bar"."Föräldra-ID" överflödigt finns det en annan skurk sätt - under förutsättning att "Foo"."Förälder-ID" är aldrig uppdaterad . Se till det, med till exempel en trigger.

Du kan fejka en IMMUTABLE funktion:

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

Jag schemakvalificerade tabellnamnet för att vara säker, förutsatt att public är . Anpassa dig till ditt schema.

Mer:

Använd det sedan i undantagsbegränsningen:

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

Samtidigt som du sparar en redundant int4 kolumn blir begränsningen dyrare att verifiera och hela lösningen beror på fler förutsättningar.

Hantera konflikter

Du kan slå in INSERT och UPPDATERA till en plpgsql-funktion och fånga möjliga undantag från undantagsbegränsningen (23P01 exclusion_violation ) för att hantera det på något sätt.

INSERT ...

EXCEPTION
    WHEN exclusion_violation
    THEN  -- handle conflict

Komplett kodexempel:

Hantera konflikter i Postgres 9.5

I Postgres 9.5 du kan hantera INSERT direkt med den nya "UPSERT"-implementeringen. Dokumentationen:

Men:

Men du kan fortfarande använda ON CONFLICT DO INGENTING , och undviker därmed möjlig exclusion_violation undantag. Kontrollera bara om några rader faktiskt har uppdaterats, vilket är billigare:

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

IF NOT FOUND THEN
   -- handle conflict
END IF;

Det här exemplet begränsar kontrollen till den givna uteslutningsrestriktionen. (Jag namngav begränsningen uttryckligen för detta ändamål i tabelldefinitionen ovan.) Andra möjliga undantag fångas inte upp.




  1. Generera DDL med Oracle SQL Developer för att inkludera främmande nycklar

  2. Välj, ändra och infoga i samma tabell

  3. FileUpload kontroll i visual studio och spara bilden i MySql-databasen

  4. Fel när utdata från en mysql-fråga skickades till en csv-fil