Der rechtschaffene Weg
Vielleicht möchten Sie die Normalisierung noch einmal überdenken Ihr Schema. Es ist nicht notwendig, dass jeder "selbst bei der einfachsten Frage mitmacht" . Erstellen Sie eine VIEW
dafür.
Die Tabelle könnte so aussehen:
CREATE TABLE hostname (
hostname_id serial PRIMARY KEY
, host_id int REFERENCES host(host_id) ON UPDATE CASCADE ON DELETE CASCADE
, hostname text UNIQUE
);
Der Ersatz-Primärschlüssel hostname_id
ist optional . Ich habe lieber einen. In Ihrem Fall hostname
könnte der Primärschlüssel sein. Aber viele Operationen sind mit einer einfachen, kleinen integer
schneller Schlüssel. Erstellen Sie eine Fremdschlüsseleinschränkung, um mit der Tabelle host
zu verknüpfen .
Erstellen Sie eine Ansicht wie diese:
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+
Beginnend mit Seite 9.1 , der Primärschlüssel in GROUP BY
deckt alle Spalten dieser Tabelle im SELECT
ab aufführen. Die Versionshinweise für Version 9.1:
Nicht-GROUP BY
zulassen Spalten in der Abfragezielliste, wenn der Primärschlüssel in GROUP BY
angegeben ist Klausel
Abfragen können die Ansicht wie eine Tabelle verwenden. Die Suche nach einem Hostnamen wird viel sein schneller so:
SELECT *
FROM host h
JOIN hostname hn USING (host_id)
WHERE hn.hostname = 'foobar';
In Postgres 9.2+ Ein mehrspaltiger Index wäre sogar noch besser, wenn Sie einen Nur-Index-Scan erhalten könnten daraus:
CREATE INDEX hn_multi_idx ON hostname (hostname, host_id);
Ab Postgres 9.3 , könnten Sie eine MATERIALIZED VIEW
verwenden , wenn es die Umstände zulassen. Vor allem, wenn Sie viel öfter lesen als schreiben.
Die dunkle Seite (was Sie eigentlich gefragt haben)
Wenn ich dich nicht vom rechtschaffenen Weg überzeugen kann, helfe ich auch auf der dunklen Seite. Ich bin flexibel. :)
Hier ist eine Demo, wie man die Eindeutigkeit von Hostnamen erzwingt. Ich verwende eine Tabelle hostname
zum Sammeln von Hostnamen und einen Trigger auf die Tabelle host
um es aktuell zu halten. Eindeutige Verstöße lösen eine Ausnahme aus und brechen die Operation ab.
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;
Auslöser:
CREATE TRIGGER host_insupdelbef
BEFORE INSERT OR DELETE OR UPDATE OF hostnames ON host
FOR EACH ROW EXECUTE PROCEDURE trg_host_insupdelbef();
SQL-Geige mit Probelauf.
Verwenden Sie einen GIN-Index in der Array-Spalte host.hostnames
und Array-Operatoren um damit zu arbeiten:
- Warum wird mein PostgreSQL-Array-Index nicht verwendet (Rails 4)?
- Überprüfen Sie, ob ein bestimmtes Array von Werten in einem Postgres-Array vorhanden ist