PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Zapytanie Postgresql 9.4 staje się coraz wolniejsze podczas dołączania do TSTZRANGE za pomocą &&

Ograniczenie wykluczenia

Proponuję zamiast tego użyć ograniczenia wykluczenia, które jest znacznie prostsze, bezpieczniejsze i szybsze:

Musisz zainstalować dodatkowy moduł btree_gist pierwszy. Zobacz instrukcje i wyjaśnienia w tej powiązanej odpowiedzi:

Musisz dołączyć "Identyfikator rodzica" w tabeli "Bar" niepotrzebnie, co będzie niewielką ceną do zapłacenia. Definicje tabel mogą wyglądać tak:

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

Zmieniłem również typ danych dla "Bar"."FooID" z int8 do int4 . Odwołuje się do "Foo"."FooID" , który jest seryjnym , czyli int4 . Użyj pasującego typu int4 (lub po prostu liczba całkowita ) z kilku powodów, jednym z nich jest wydajność.

Nie potrzebujesz już wyzwalacza (przynajmniej nie do tego zadania) i nie tworzysz indeksu "Bar_FooID_Timerange_idx" więcej, ponieważ jest tworzony niejawnie przez ograniczenie wykluczenia.

Indeks btree na ("ParentID", "FooID") najprawdopodobniej jednak się przyda:

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

Powiązane:

Wybrałem UNIQUE („ParentID”, „FooID”) a nie odwrotnie z jakiegoś powodu, ponieważ istnieje inny indeks z wiodącym "FooID" w dowolnej tabeli:

Na marginesie:Nigdy nie używam podwójnie cudzysłowu CaMeL -identyfikatory wielkości liter w Postgresie. Robię to tylko w celu zachowania zgodności z twoim układem.

Unikaj zbędnych kolumn

Jeśli nie możesz lub nie chcesz dołączyć "Bar"."ParentID" niepotrzebnie, jest jeszcze jeden łotr sposób - pod warunkiem, że "Foo"."ParentID" jest nigdy nie aktualizowany . Upewnij się o tym, na przykład za pomocą wyzwalacza.

Możesz sfałszować IMMUTABLE funkcja:

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

Aby się upewnić, zakwalifikowałem nazwę tabeli według schematu, zakładając public . Dostosuj się do swojego schematu.

Więcej:

Następnie użyj go w ograniczeniu wykluczenia:

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

Podczas zapisywania jednego nadmiarowego int4 kolumna, ograniczenie będzie droższe do zweryfikowania, a całe rozwiązanie zależy od większej liczby warunków wstępnych.

Rozwiązywanie konfliktów

Możesz zawinąć INSERT i AKTUALIZUJ do funkcji plpgsql i przechwytuje możliwe wyjątki od ograniczenia wykluczenia (23P01 exclude_violation ) aby sobie z tym poradzić.

INSERT ...

EXCEPTION
    WHEN exclusion_violation
    THEN  -- handle conflict

Pełny przykład kodu:

Rozwiąż konflikt w Postgresie 9.5

W Postgresie 9,5 możesz obsłużyć INSERT bezpośrednio z nową implementacją „UPSERT”. Dokumentacja:

Jednak:

Ale nadal możesz użyć W KONFLIKCIE NIC NIE RÓB , unikając w ten sposób możliwego exclusion_violation wyjątki. Po prostu sprawdź, czy jakieś wiersze zostały rzeczywiście zaktualizowane, co jest tańsze:

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

IF NOT FOUND THEN
   -- handle conflict
END IF;

Ten przykład ogranicza sprawdzanie do podanego ograniczenia wykluczenia. (W tym celu nazwałem ograniczenie wyraźnie w powyższej definicji tabeli.) Inne możliwe wyjątki nie są przechwytywane.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SQL :Utwórz pełny rekord z 2 tabel

  2. SQLAlchemy — stan licznika to prawda

  3. Jak porównać dwie tablice i wybrać tylko niepasujące elementy W postgres

  4. Jak rzutować typ bajta do podwójnej precyzji

  5. Wartość „CONTINUE” nie jest błędem aspektu podczas uruchamiania liquibase