Den rättfärdiga vägen
Du kanske vill ompröva normalisering ditt schema. Det är inte nödvändigt för alla att "gå med för ens den enklaste frågan" . Skapa en VIEW
för det.
Tabell kan se ut så här:
CREATE TABLE hostname (
hostname_id serial PRIMARY KEY
, host_id int REFERENCES host(host_id) ON UPDATE CASCADE ON DELETE CASCADE
, hostname text UNIQUE
);
Den primära surrogatnyckeln hostname_id
är valfritt . Jag föredrar att ha en. I ditt fall hostname
kan vara den primära nyckeln. Men många operationer är snabbare med ett enkelt, litet integer
nyckel. Skapa en främmande nyckel-begränsning för att länka till tabellen host
.
Skapa en vy så här:
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örjar med sidan 9.1 , den primära nyckeln i GROUP BY
täcker alla kolumner i den tabellen i SELECT
lista. Utgivningsinformationen för version 9.1:
Tillåt icke-
GROUP BY
kolumner i frågemållistan när primärnyckeln anges iGROUP BY
klausul
Frågor kan använda vyn som en tabell. Att söka efter ett värdnamn blir mycket snabbare så här:
SELECT *
FROM host h
JOIN hostname hn USING (host_id)
WHERE hn.hostname = 'foobar';
I Postgres 9.2+ ett index med flera kolumner skulle vara ännu bättre om du kan få en endast indexsökning ur det:
CREATE INDEX hn_multi_idx ON hostname (hostname, host_id);
Börjar med Postgres 9.3 , kan du använda en MATERIALIZED VIEW
, om omständigheterna tillåter. Speciellt om du läser mycket oftare än du skriver till bordet.
Den mörka sidan (det du faktiskt frågade om)
Om jag inte kan övertyga dig om den rättfärdiga vägen, hjälper jag också till på den mörka sidan. Jag är flexibel. :)
Här är en demonstration av hur man upprätthåller unika värdnamn. Jag använder en tabell hostname
för att samla in värdnamn och en trigger på tabellen host
för att hålla den uppdaterad. Unika överträdelser skapar ett undantag och avbryter operationen.
CREATE TABLE host(hostnames text[]);
CREATE TABLE hostname(hostname text PRIMARY KEY); -- pk enforces uniqueness
Triggerfunktion:
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;
Utlösare:
CREATE TRIGGER host_insupdelbef
BEFORE INSERT OR DELETE OR UPDATE OF hostnames ON host
FOR EACH ROW EXECUTE PROCEDURE trg_host_insupdelbef();
SQL Fiddle med provkörning.
Använd ett GIN-index i arraykolumnen host.hostnames
och matrisoperatorer att arbeta med det:
- Varför används inte mitt PostgreSQL-arrayindex (Rails 4)?
- Kontrollera om någon av en given matris med värden finns i en Postgres-matris