Jalan lurus
Anda mungkin ingin mempertimbangkan kembali menormalkan skema Anda. Semua orang tidak perlu "bergabung bahkan untuk kueri yang paling sederhana" . Buat VIEW
untuk itu.
Tabel dapat terlihat seperti ini:
CREATE TABLE hostname (
hostname_id serial PRIMARY KEY
, host_id int REFERENCES host(host_id) ON UPDATE CASCADE ON DELETE CASCADE
, hostname text UNIQUE
);
Kunci utama pengganti hostname_id
adalah opsional . Saya lebih suka memiliki satu. Dalam kasus Anda hostname
bisa menjadi kunci utama. Tetapi banyak operasi lebih cepat dengan integer
yang sederhana dan kecil kunci. Buat batasan kunci asing untuk menautkan ke tabel host
.
Buat tampilan seperti ini:
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+
Dimulai dengan hal 9.1 , kunci utama dalam GROUP BY
mencakup semua kolom tabel itu di SELECT
daftar. Catatan rilis untuk versi 9.1:
Izinkan non-
GROUP BY
kolom dalam daftar target kueri saat kunci utama ditentukan diGROUP BY
klausa
Kueri dapat menggunakan tampilan seperti tabel. Mencari nama host akan banyak lebih cepat dengan cara ini:
SELECT *
FROM host h
JOIN hostname hn USING (host_id)
WHERE hn.hostname = 'foobar';
Di Postgres 9.2+ indeks multikolom akan lebih baik jika Anda bisa mendapatkan scan hanya indeks dari itu:
CREATE INDEX hn_multi_idx ON hostname (hostname, host_id);
Dimulai dengan Postgres 9.3 , Anda dapat menggunakan MATERIALIZED VIEW
, keadaan memungkinkan. Terutama jika Anda lebih sering membaca daripada menulis di meja.
Sisi gelap (apa yang sebenarnya Anda tanyakan)
Jika saya tidak dapat meyakinkan Anda tentang jalan yang benar, saya akan membantu di sisi gelap juga. saya fleksibel. :)
Berikut adalah demo cara menegakkan keunikan nama host. Saya menggunakan tabel hostname
untuk mengumpulkan nama host dan pemicu di tabel host
untuk tetap up to date. Pelanggaran unik menimbulkan pengecualian dan membatalkan operasi.
CREATE TABLE host(hostnames text[]);
CREATE TABLE hostname(hostname text PRIMARY KEY); -- pk enforces uniqueness
Fungsi pemicu:
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;
Pemicu:
CREATE TRIGGER host_insupdelbef
BEFORE INSERT OR DELETE OR UPDATE OF hostnames ON host
FOR EACH ROW EXECUTE PROCEDURE trg_host_insupdelbef();
SQL Fiddle dengan uji coba.
Gunakan indeks GIN pada kolom array host.hostnames
dan operator larik untuk bekerja dengannya:
- Mengapa indeks array PostgreSQL saya tidak digunakan (Rails 4)?
- Periksa apakah ada larik nilai tertentu yang ada dalam larik Postgres