sql >> Database teknologi >  >> RDS >> PostgreSQL

Postgresql 9.4-forespørgslen bliver gradvist langsommere, når du tilslutter TSTZRANGE med &&

Eksklusionsbegrænsning

Jeg foreslår, at du i stedet bruger en udelukkelsesbegrænsning, som er meget enklere, sikrere og hurtigere:

Du skal installere det ekstra modul btree_gist først. Se instruktioner og forklaring i dette relaterede svar:

Og du skal inkludere "Forældre-ID" i tabellen "Bar" overflødigt, hvilket vil være en lille pris at betale. Tabeldefinitioner kunne se sådan ud:

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

Jeg har også ændret datatypen for "Bar"."FooID" fra int8 til int4 . Den refererer til "Foo"."FooID" , som er en serie , dvs. int4 . Brug matchningstypen int4 (eller bare heltal ) af flere årsager, en af ​​dem er ydeevne.

Du behøver ikke længere en trigger (i hvert fald ikke til denne opgave), og du opretter ikke indekset "Bar_FooID_Timerange_idx" mere, da det er skabt implicit af udelukkelsesbegrænsningen.

Et btree-indeks på ("ParentID", "FooID") vil dog højst sandsynligt være nyttig:

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

Relateret:

Jeg valgte UNIQUE ("Forældre-ID", "FooID") og ikke omvendt af en grund, da der er et andet indeks med førende "FooID" i begge tabeller:

Bortset fra:Jeg bruger aldrig CaMeL i dobbelte citater -case-id'er i Postgres. Jeg gør det kun her for at overholde dit layout.

Undgå overflødig kolonne

Hvis du ikke kan eller vil inkludere "Bar"."Forældre-ID" overflødigt er der en anden slyngel måde - på den betingelse, at "Foo"."Forældre-ID" er aldrig opdateret . Sørg for det, med for eksempel en trigger.

Du kan forfalske 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;

Jeg skemakvalificerede tabelnavnet for at være sikker, forudsat offentlig . Tilpas til dit skema.

Mere:

Brug det derefter i ekskluderingsbegrænsningen:

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

Mens du gemmer en redundant int4 kolonne, vil begrænsningen være dyrere at verificere, og hele løsningen afhænger af flere forudsætninger.

Håndter konflikter

Du kan ombryde INSERT og OPDATERING ind i en plpgsql-funktion og fange mulige undtagelser fra ekskluderingsbegrænsningen (23P01 exclusion_violation ) for at håndtere det på en eller anden måde.

INSERT ...

EXCEPTION
    WHEN exclusion_violation
    THEN  -- handle conflict

Komplet kodeeksempel:

Håndter konflikt i Postgres 9.5

I Postgres 9.5 du kan håndtere INSERT direkte med den nye "UPSERT" implementering. Dokumentationen:

Dog:

Men du kan stadig bruge ON CONFLICT DO NOTTHING , og dermed undgå mulig exclusion_violation undtagelser. Bare tjek, om nogle rækker rent faktisk blev opdateret, hvilket er billigere:

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

IF NOT FOUND THEN
   -- handle conflict
END IF;

Dette eksempel begrænser kontrollen til den givne udelukkelsesbegrænsning. (Jeg navngav begrænsningen eksplicit til dette formål i tabeldefinitionen ovenfor.) Andre mulige undtagelser er ikke fanget.




  1. Brug af MySQL 'POINT' og PHP til at indsætte bredde- og længdegradspunkter gennem en formular

  2. group_concat ydeevneproblem i MySQL

  3. Spejl specifikke tabeller i postgreSQL

  4. InnoDB:Masseindsættelse ved hjælp af transaktion ELLER kombinere flere forespørgsler?