Khi chỉnh sửa của bạn được làm rõ, bạn đã cài đặt tiện ích mở rộng btree_gist
. Nếu không có nó, ví dụ sẽ không thành công tại name WITH =
.
CREATE EXTENSION btree_gist;
Các lớp toán tử được cài đặt bởi btree_gist
bao gồm nhiều nhà khai thác. Thật không may, &
nhà điều hành không nằm trong số đó. Rõ ràng là vì nó không trả về boolean
điều mà một nhà điều hành dự kiến sẽ đủ điều kiện.
Giải pháp thay thế
Tôi sẽ sử dụng kết hợp b-cây chỉ mục nhiều cột (cho tốc độ) và một trình kích hoạt thay vì. Hãy xem xét bản trình diễn này, được thử nghiệm trên PostgreSQL 9.1 :
CREATE TABLE t (
name text
,value bit(8)
);
INSERT INTO t VALUES ('a', B'10101010');
CREATE INDEX t_name_value_idx ON t (name, value);
CREATE OR REPLACE FUNCTION trg_t_name_value_inversion_prohibited()
RETURNS trigger AS
$func$
BEGIN
IF EXISTS (
SELECT 1 FROM t
WHERE (name, value) = (NEW.name, ~ NEW.value) -- example: exclude inversion
) THEN
RAISE EXCEPTION 'Your text here!';
END IF;
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
CREATE TRIGGER insup_bef_t_name_value_inversion_prohibited
BEFORE INSERT OR UPDATE OF name, value -- only involved columns relevant!
ON t
FOR EACH ROW
EXECUTE PROCEDURE trg_t_name_value_inversion_prohibited();
INSERT INTO t VALUES ('a', ~ B'10101010'); -- fails with your error msg.
-
Phần mở rộng
btree_gist
là không yêu cầu trong trường hợp này. -
Tôi đã hạn chế trình kích hoạt CHÈN hoặc CẬP NHẬT các cột có liên quan để đạt hiệu quả.
-
Một ràng buộc kiểm tra sẽ không hoạt động. Tôi trích dẫn hướng dẫn về
TẠO BẢNG
:Nhấn mạnh của tôi:
Sẽ hoạt động rất tốt, thực sự tốt hơn so với ràng buộc loại trừ, bởi vì việc duy trì chỉ mục b-tree rẻ hơn chỉ mục GiST. Và tra cứu với =
cơ bản toán tử sẽ nhanh hơn tra cứu giả định với &
nhà điều hành.
Giải pháp này không an toàn như một ràng buộc loại trừ, vì trình kích hoạt có thể dễ dàng bị phá vỡ hơn - ví dụ:trong lần kích hoạt tiếp theo trên cùng một sự kiện hoặc nếu trình kích hoạt bị tắt tạm thời. Hãy chuẩn bị để kiểm tra thêm trên toàn bộ bảng nếu các điều kiện đó được áp dụng.
Điều kiện phức tạp hơn
Trình kích hoạt mẫu chỉ bắt được sự đảo ngược của giá trị
. Như bạn đã làm rõ trong nhận xét của mình, bạn thực sự cần một điều kiện như sau:
IF EXISTS (
SELECT 1 FROM t
WHERE name = NEW.name
AND value & NEW.value <> B'00000000'::bit(8)
) THEN
Điều kiện này đắt hơn một chút, nhưng vẫn có thể sử dụng một chỉ mục. Chỉ mục nhiều cột từ phía trên sẽ hoạt động - nếu bạn vẫn cần nó. Hoặc, hiệu quả hơn một chút, một chỉ mục đơn giản trên tên:
CREATE INDEX t_name_idx ON t (name);
Như bạn đã nhận xét, chỉ có thể có tối đa 8 hàng riêng biệt cho mỗi name
, ít hơn trong thực tế. Vì vậy, điều này vẫn sẽ nhanh chóng.
Hiệu suất INSERT cao nhất
Nếu INSERT
hiệu suất là điều tối quan trọng, đặc biệt nếu nhiều lần cố gắng CHÈN không đạt được điều kiện, bạn có thể làm thêm:tạo chế độ xem cụ thể hóa giá trị
được tổng hợp trước per name
:
CREATE TABLE mv_t AS
SELECT name, bit_or(value) AS value
FROM t
GROUP BY 1
ORDER BY 1;
tên
được đảm bảo là duy nhất ở đây. Tôi muốn sử dụng CHÌA KHÓA CHÍNH
trên name
để cung cấp chỉ mục mà chúng tôi đang theo dõi:
ALTER TABLE mv_t SET (fillfactor=90);
ALTER TABLE mv_t
ADD CONSTRAINT mv_t_pkey PRIMARY KEY(name) WITH (fillfactor=90);
Sau đó, INSERT
của bạn có thể trông như thế này:
WITH i(n,v) AS (SELECT 'a'::text, B'10101010'::bit(8))
INSERT INTO t (name, value)
SELECT n, v
FROM i
LEFT JOIN mv_t m ON m.name = i.n
AND m.value & i.v <> B'00000000'::bit(8)
WHERE m.n IS NULL; -- alternative syntax for EXISTS (...)
fillfactor
chỉ hữu ích nếu bảng của bạn nhận được nhiều cập nhật.
Cập nhật các hàng trong dạng xem cụ thể hóa trong TRIGGER SAU KHI CHÈN HOẶC CẬP NHẬT tên, giá trị HOẶC XÓA
để giữ cho nó hiện tại. Chi phí của các đối tượng bổ sung phải được cân nhắc với lợi ích. Phần lớn phụ thuộc vào tải thông thường của bạn.