sql >> Database >  >> RDS >> PostgreSQL

Postgresql 9.4-query wordt steeds langzamer bij deelname aan TSTZRANGE met &&

Uitsluitingsbeperking

Ik raad u aan in plaats daarvan een uitsluitingsbeperking te gebruiken, die veel eenvoudiger, veiliger en sneller is:

U moet de extra module btree_gist eerst. Zie instructies en uitleg in dit gerelateerde antwoord:

En je moet "ParentID" . opnemen in de tabel "balk" overbodig, wat een kleine prijs zal zijn om te betalen. Tabeldefinities kunnen er als volgt uitzien:

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

Ik heb ook het gegevenstype gewijzigd voor "Bar"."FooID" van int8 naar int4 . Het verwijst naar "Foo"."FooID" , wat een serie . is , d.w.z. int4 . Gebruik het overeenkomende type int4 (of gewoon geheel getal ) om verschillende redenen, waaronder prestatie.

Je hebt geen trigger meer nodig (tenminste niet voor deze taak), en je maakt de index "Bar_FooID_Timerange_idx" niet aan niet meer, omdat het impliciet is gemaakt door de uitsluitingsbeperking.

Een btree-index op ("ParentID", "FooID") zal echter hoogstwaarschijnlijk nuttig zijn:

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

Gerelateerd:

Ik koos UNIEK ("ParentID", "FooID") en niet omgekeerd met een reden, aangezien er een andere index is met leidende "FooID" in beide tabellen:

Terzijde:Ik gebruik nooit dubbel geciteerde CaMeL -case-ID's in Postgres. Ik doe het hier alleen om te voldoen aan uw lay-out.

Vermijd overbodige kolom

Als u "Bar"."ParentID" . niet kunt of wilt opnemen overbodig, er is nog een schurk manier - op voorwaarde dat "Foo"."ParentID" is nooit bijgewerkt . Zorg daarvoor, bijvoorbeeld met een trigger.

Je kunt een IMMUTABLE fake faken functie:

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

Ik heb de tabelnaam schematisch gekwalificeerd om er zeker van te zijn, ervan uitgaande dat public . Aanpassen aan uw schema.

Meer:

Gebruik het dan in de uitsluitingsbeperking:

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

Tijdens het opslaan van een overbodige int4 kolom, zal de beperking duurder zijn om te verifiëren en de hele oplossing hangt af van meer randvoorwaarden.

Conflicten afhandelen

Je zou INSERT kunnen omwikkelen en UPDATE omzetten in een plpgsql-functie en mogelijke uitzonderingen van de uitsluitingsbeperking opvangen (23P01 exception_violation ) om het op de een of andere manier aan te pakken.

INSERT ...

EXCEPTION
    WHEN exclusion_violation
    THEN  -- handle conflict

Compleet codevoorbeeld:

Conflict in Postgres 9.5 afhandelen

In Postgres 9.5 u kunt omgaan met INSERT direct met de nieuwe "UPSERT" implementatie. De documentatie:

Maar:

Maar je kunt nog steeds OP CONFLICT NIETS DOEN . gebruiken , waardoor mogelijke exclusion_violation . wordt vermeden uitzonderingen. Controleer gewoon of er rijen zijn bijgewerkt, wat goedkoper is:

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

IF NOT FOUND THEN
   -- handle conflict
END IF;

In dit voorbeeld wordt de controle beperkt tot de gegeven uitsluitingsbeperking. (Ik heb de beperking expliciet voor dit doel genoemd in de bovenstaande tabeldefinitie.) Andere mogelijke uitzonderingen worden niet opgevangen.




  1. Proberen te controleren of de gebruikersnaam al bestaat in de MySQL-database met behulp van PHP

  2. verschil tussen localhost en postgres voor host in docker

  3. zoombestand uploaden naar postgres bytea kolom kolom is bytea maar expressie is van het type bigint

  4. Bereken het aantal kalendermaanden dat twee datums omvatten