Het rechtvaardige pad
Misschien wil je het normaliseren heroverwegen uw schema. Het is niet voor iedereen nodig om "mee te doen voor zelfs de eenvoudigste vraag" . Maak een VIEW
daarvoor.
Tabel kan er als volgt uitzien:
CREATE TABLE hostname (
hostname_id serial PRIMARY KEY
, host_id int REFERENCES host(host_id) ON UPDATE CASCADE ON DELETE CASCADE
, hostname text UNIQUE
);
De surrogaat primaire sleutel hostname_id
is optioneel . Ik heb er liever een. In jouw geval hostname
zou de primaire sleutel kunnen zijn. Maar veel bewerkingen zijn sneller met een eenvoudige, kleine integer
sleutel. Maak een externe sleutelbeperking om te linken naar de tabel host
.
Maak een weergave als deze:
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 met pg 9.1 , de primaire sleutel in de GROUP BY
omvat alle kolommen van die tabel in de SELECT
lijst. De release-opmerkingen voor versie 9.1:
Niet-
GROUP BY
Allow toestaan kolommen in de zoekdoellijst wanneer de primaire sleutel is opgegeven in deGROUP BY
clausule
Query's kunnen de weergave gebruiken als een tabel. Zoeken naar een hostnaam is veel sneller op deze manier:
SELECT *
FROM host h
JOIN hostname hn USING (host_id)
WHERE hn.hostname = 'foobar';
In Postgres 9.2+ een index met meerdere kolommen zou nog beter zijn als u een alleen-index scan . kunt krijgen eruit:
CREATE INDEX hn_multi_idx ON hostname (hostname, host_id);
Beginnend met Postgres 9.3 , kunt u een MATERIALIZED VIEW
. gebruiken , omstandigheden het toelaten. Vooral als je veel vaker leest dan dat je naar de tafel schrijft.
De donkere kant (wat je eigenlijk vroeg)
Als ik je niet kan overtuigen van het rechtvaardige pad, zal ik ook aan de donkere kant helpen. Ik ben flexibel. :)
Hier is een demo hoe u de uniciteit van hostnamen kunt afdwingen. Ik gebruik een tabel hostname
om hostnamen en een trigger op de tafel te verzamelen host
om het up-to-date te houden. Unieke schendingen leiden tot een uitzondering en breken de bewerking af.
CREATE TABLE host(hostnames text[]);
CREATE TABLE hostname(hostname text PRIMARY KEY); -- pk enforces uniqueness
Trigger-functie:
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;
Trigger:
CREATE TRIGGER host_insupdelbef
BEFORE INSERT OR DELETE OR UPDATE OF hostnames ON host
FOR EACH ROW EXECUTE PROCEDURE trg_host_insupdelbef();
SQL Fiddle met proefdraaien.
Gebruik een GIN-index in de matrixkolom host.hostnames
en matrixoperatoren om ermee te werken:
- Waarom wordt mijn PostgreSQL-array-index niet gebruikt (Rails 4)?
- Controleer of een van een bepaalde array van waarden aanwezig is in een Postgres-array