sql >> Databáze >  >> RDS >> PostgreSQL

Dotaz Postgresql 9.4 se postupně zpomaluje, když se připojujete k TSTZRANGE pomocí &&

Omezení vyloučení

Navrhuji, abyste místo toho použili omezení vyloučení, které je mnohem jednodušší, bezpečnější a rychlejší:

Musíte nainstalovat doplňkový modul btree_gist První. Viz pokyny a vysvětlení v této související odpovědi:

A musíte zahrnout "ParentID" v tabulce "Bar" nadbytečně, což bude malá cena. Definice tabulek mohou vypadat takto:

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

Také jsem změnil typ dat pro "Bar"."FooID" z int8 na int4 . Odkazuje na "Foo"."FooID" , což je serial , tj. int4 . Použijte typ shody int4 (nebo jen integer ) z několika důvodů, jedním z nich je výkon.

Už nepotřebujete spouštěč (alespoň ne pro tento úkol) a nevytváříte index "Bar_FooID_Timerange_idx" nic víc, protože je vytvořeno implicitně omezením vyloučení.

Index btree na ("ParentID", "FooID") s největší pravděpodobností však bude užitečné:

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

Související:

Vybral jsem UNIQUE ("ParentID", "FooID") a ne naopak z nějakého důvodu, protože existuje další index s úvodním "FooID" v obou tabulkách:

Stranou:Nikdy nepoužívám CaMeL s dvojitými uvozovkami -identifikátory případu v Postgresu. Dělám to jen proto, abych vyhověl vašemu rozvržení.

Vyhněte se nadbytečným sloupcům

Pokud nemůžete nebo nechcete zahrnout "Bar"."ParentID" nadbytečně je tu další darebák způsobem - za podmínky, že "Foo"."ParentID" nikdy aktualizováno . Ujistěte se o tom, například pomocí spouštěče.

Můžete předstírat IMMUTABLE funkce:

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

Pro jistotu jsem název tabulky kvalifikoval podle schématu, za předpokladu public . Přizpůsobte se svému schématu.

Více:

Pak jej použijte v omezení vyloučení:

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

Při ukládání jednoho nadbytečného int4 sloupec, bude ověření nákladnější a celé řešení závisí na více předpokladech.

Řešení konfliktů

Můžete zalomit INSERT a UPDATE do funkce plpgsql a zachytit možné výjimky z omezení vyloučení (23P01 exclusion_violation ), abych to nějak zvládl.

INSERT ...

EXCEPTION
    WHEN exclusion_violation
    THEN  -- handle conflict

Kompletní příklad kódu:

Řešení konfliktů v Postgres 9.5

V Postgres 9.5 můžete zpracovat INSERT přímo s novou implementací "UPSERT". Dokumentace:

Nicméně:

Stále však můžete použít ON CONFLICT DO NOTHING , čímž se vyhnete možnému exclusion_violation výjimky. Stačí zkontrolovat, zda byly nějaké řádky skutečně aktualizovány, což je levnější:

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

IF NOT FOUND THEN
   -- handle conflict
END IF;

Tento příklad omezuje kontrolu na danou podmínku vyloučení. (Omezení jsem výslovně pojmenoval pro tento účel v definici tabulky výše.) Další možné výjimky nejsou zachyceny.




  1. Jak používat klauzuli hromadného sběru s příkazem SELECT INTO v databázi Oracle

  2. Jak změnit cizí klíč tabulky MySQL pomocí příkazového řádku

  3. Příkaz SQL - Jak lze zlepšit rychlost indexováním

  4. Entity Framework s Mysql Index