Con đường chính nghĩa
Bạn có thể muốn xem xét lại chuẩn hóa lược đồ của bạn. Mọi người không nhất thiết phải "tham gia ngay cả với truy vấn đơn giản nhất" . Tạo VIEW
cho điều đó.
Bảng có thể trông như thế này:
CREATE TABLE hostname (
hostname_id serial PRIMARY KEY
, host_id int REFERENCES host(host_id) ON UPDATE CASCADE ON DELETE CASCADE
, hostname text UNIQUE
);
Khóa chính thay thế hostname_id
là tùy chọn . Tôi thích có một cái hơn. Trong trường hợp của bạn hostname
có thể là khóa chính. Nhưng nhiều thao tác nhanh hơn với integer
đơn giản, nhỏ Chìa khóa. Tạo ràng buộc khóa ngoại để liên kết với bảng host
.
Tạo một dạng xem như thế này:
CREATE VIEW v_host AS
SELECT h.*
, array_agg(hn.hostname) AS hostnames
-- , string_agg(hn.hostname, ', ') AS hostnames -- text instead of array
FROM host h
JOIN hostname hn USING (host_id)
GROUP BY h.host_id; -- works in v9.1+
Bắt đầu với pg 9.1 , khóa chính trong GROUP BY
bao gồm tất cả các cột của bảng đó trong SELECT
danh sách. Các ghi chú phát hành cho phiên bản 9.1:
Cho phép không phải
GROUP BY
trong danh sách đích truy vấn khi khóa chính được chỉ định trongGROUP BY
mệnh đề
Các truy vấn có thể sử dụng dạng xem như một bảng. Tìm kiếm tên máy chủ sẽ nhiều nhanh hơn theo cách này:
SELECT *
FROM host h
JOIN hostname hn USING (host_id)
WHERE hn.hostname = 'foobar';
Trong Postgres 9.2+ chỉ mục nhiều cột sẽ còn tốt hơn nếu bạn có thể nhận được bản quét chỉ chỉ mục ra khỏi nó:
CREATE INDEX hn_multi_idx ON hostname (hostname, host_id);
Bắt đầu với Postgres 9.3 , bạn có thể sử dụng MATERIALIZED VIEW
, hoàn cảnh cho phép. Đặc biệt nếu bạn đọc thường xuyên hơn nhiều so với việc bạn ghi vào bảng.
Mặt tối (những gì bạn thực sự hỏi)
Nếu tôi không thể thuyết phục bạn về con đường chính nghĩa, tôi cũng sẽ hỗ trợ cho mặt tối. Tôi linh hoạt. :)
Đây là bản demo cách thực thi tính duy nhất của tên máy chủ. Tôi sử dụng một bảng hostname
để thu thập tên máy chủ và trình kích hoạt trên bảng host
để giữ cho nó được cập nhật. Các vi phạm duy nhất đưa ra một ngoại lệ và hủy bỏ hoạt động.
CREATE TABLE host(hostnames text[]);
CREATE TABLE hostname(hostname text PRIMARY KEY); -- pk enforces uniqueness
Chức năng kích hoạt:
CREATE OR REPLACE FUNCTION trg_host_insupdelbef()
RETURNS trigger AS
$func$
BEGIN
-- split UPDATE into DELETE & INSERT
IF TG_OP = 'UPDATE' THEN
IF OLD.hostnames IS DISTINCT FROM NEW.hostnames THEN -- keep going
ELSE RETURN NEW; -- exit, nothing to do
END IF;
END IF;
IF TG_OP IN ('DELETE', 'UPDATE') THEN
DELETE FROM hostname h
USING unnest(OLD.hostnames) d(x)
WHERE h.hostname = d.x;
IF TG_OP = 'DELETE' THEN RETURN OLD; -- exit, we are done
END IF;
END IF;
-- control only reaches here for INSERT or UPDATE (with actual changes)
INSERT INTO hostname(hostname)
SELECT h
FROM unnest(NEW.hostnames) h;
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
Kích hoạt:
CREATE TRIGGER host_insupdelbef
BEFORE INSERT OR DELETE OR UPDATE OF hostnames ON host
FOR EACH ROW EXECUTE PROCEDURE trg_host_insupdelbef();
SQL Fiddle với quá trình chạy thử nghiệm.
Sử dụng chỉ mục GIN trên cột mảng host.hostnames
và toán tử mảng để làm việc với nó:
- Tại sao chỉ mục mảng PostgreSQL của tôi không được sử dụng (Rails 4)?
- Kiểm tra xem có bất kỳ mảng giá trị nhất định nào trong mảng Postgres không