PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Truy vấn Postgresql 9.4 chậm dần khi tham gia TSTZRANGE với &&

Ràng buộc loại trừ

Tôi khuyên bạn nên sử dụng một ràng buộc loại trừ để thay thế, điều này đơn giản hơn, an toàn hơn và nhanh hơn nhiều:

Bạn cần cài đặt mô-đun bổ sung btree_gist Đầu tiên. Xem hướng dẫn và giải thích trong câu trả lời liên quan này:

Và bạn cần bao gồm "ParentID" trong bảng "Bar" dư thừa, đó sẽ là một cái giá nhỏ phải trả. Định nghĩa bảng có thể trông giống như sau:

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

Tôi cũng đã thay đổi kiểu dữ liệu cho "Bar"."FooID" từ int8 thành int4 . Nó tham chiếu đến "Foo"."FooID" , là một serial , tức là int4 . Sử dụng loại đối sánh int4 (hoặc chỉ integer ) vì một số lý do, một trong số đó là hiệu suất.

Bạn không cần trình kích hoạt nữa (ít nhất là không phải cho tác vụ này) và bạn không tạo chỉ mục "Bar_FooID_Timerange_idx" nữa, vì nó được tạo ngầm bởi ràng buộc loại trừ.

Chỉ mục btree trên ("ParentID", "FooID") hầu hết có lẽ sẽ hữu ích, mặc dù:

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

Có liên quan:

Tôi đã chọn UNIQUE ("ParentID", "FooID") và không phải ngược lại vì một lý do, vì có một chỉ mục khác với "FooID" đứng đầu trong một trong hai bảng:

Ngoài ra: Tôi không bao giờ sử dụng CaMeL được trích dẫn kép -số nhận dạng chữ hoa trong Postgres. Tôi chỉ làm điều đó ở đây để tuân thủ bố cục của bạn.

Tránh cột dư thừa

Nếu bạn không thể hoặc sẽ không bao gồm "Bar"."ParentID" dư thừa, có một bất hảo khác cách - với điều kiện "Foo"."ParentID" không bao giờ được cập nhật . Hãy đảm bảo điều đó, với một trình kích hoạt chẳng hạn.

Bạn có thể giả mạo IMMUTABLE chức năng:

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

Tôi đã đủ điều kiện lược đồ cho tên bảng để đảm bảo, giả sử là public . Điều chỉnh cho phù hợp với giản đồ của bạn.

Thêm:

Sau đó, sử dụng nó trong ràng buộc loại trừ:

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

Trong khi lưu một int4 dư thừa , ràng buộc sẽ đắt hơn để xác minh và toàn bộ giải pháp phụ thuộc vào nhiều điều kiện tiên quyết hơn.

Xử lý xung đột

Bạn có thể bọc INSERTUPDATE vào một hàm plpgsql và bẫy các ngoại lệ có thể có từ ràng buộc loại trừ (23P01 exclusion_violation ) để xử lý nó theo cách nào đó.

INSERT ...

EXCEPTION
    WHEN exclusion_violation
    THEN  -- handle conflict

Ví dụ về mã hoàn chỉnh:

Xử lý xung đột trong Postgres 9.5

Trong Postgres 9.5 bạn có thể xử lý INSERT trực tiếp với việc triển khai "UPSERT" mới. Tài liệu:

Tuy nhiên:

Nhưng bạn vẫn có thể sử dụng ON CONFLICT DO NOTHING , do đó tránh được exclusion_violation có thể xảy ra các trường hợp ngoại lệ. Chỉ cần kiểm tra xem có hàng nào thực sự được cập nhật hay không, hàng nào rẻ hơn:

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

IF NOT FOUND THEN
   -- handle conflict
END IF;

Ví dụ này giới hạn việc kiểm tra đối với ràng buộc loại trừ đã cho. (Tôi đã đặt tên cho ràng buộc một cách rõ ràng cho mục đích này trong định nghĩa bảng ở trên.) Không bắt được các trường hợp ngoại lệ có thể có khác.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. CHAINLINK NODE:Làm cách nào để khắc phục lỗi không thể khóa ORM?

  2. Đặt application_name trên Postgres / SQLAlchemy

  3. Bảng tạm thời PostgreSQL

  4. Mới trong PostgreSQL 12:Các cột được tạo

  5. Bản cập nhật Lion đã xóa người dùng 'postgres'. Làm thế nào để khôi phục lại nó?