Il retto sentiero
Potresti voler riconsiderare la normalizzazione il tuo schema Non è necessario che tutti "partecipino anche alla domanda più semplice" . Crea un VIEW
per quello.
La tabella potrebbe assomigliare a questa:
CREATE TABLE hostname (
hostname_id serial PRIMARY KEY
, host_id int REFERENCES host(host_id) ON UPDATE CASCADE ON DELETE CASCADE
, hostname text UNIQUE
);
La chiave primaria surrogata hostname_id
è opzionale . Preferisco averne uno. Nel tuo caso hostname
potrebbe essere la chiave primaria Ma molte operazioni sono più veloci con un semplice integer
chiave. Crea un vincolo di chiave esterna da collegare alla tabella host
.
Crea una vista come questa:
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+
A partire da pagina 9.1 , la chiave primaria nel GROUP BY
copre tutte le colonne di quella tabella in SELECT
elenco. Le note di rilascio per la versione 9.1:
Consenti non GROUP BY
colonne nell'elenco di destinazione della query quando la chiave primaria è specificata in GROUP BY
clausola
Le query possono utilizzare la vista come una tabella. La ricerca di un nome host sarà molto più veloce in questo modo:
SELECT *
FROM host h
JOIN hostname hn USING (host_id)
WHERE hn.hostname = 'foobar';
In Postgres 9.2+ un indice multicolonna sarebbe ancora meglio se puoi ottenere una scansione solo indice fuori di esso:
CREATE INDEX hn_multi_idx ON hostname (hostname, host_id);
A partire da Postgres 9.3 , potresti usare una MATERIALIZED VIEW
, circostanze permettendo. Soprattutto se leggi molto più spesso di quanto scrivi al tavolo.
Il lato oscuro (quello che hai effettivamente chiesto)
Se non riesco a convincerti della retta via, assisterò anche sul lato oscuro. Sono flessibile. :)
Ecco una demo su come rafforzare l'unicità dei nomi host. Uso una tabella hostname
per raccogliere nomi host e un trigger sulla tabella host
per tenerlo aggiornato. Violazioni uniche generano un'eccezione e interrompono l'operazione.
CREATE TABLE host(hostnames text[]);
CREATE TABLE hostname(hostname text PRIMARY KEY); -- pk enforces uniqueness
Funzione di attivazione:
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;
Attivazione:
CREATE TRIGGER host_insupdelbef
BEFORE INSERT OR DELETE OR UPDATE OF hostnames ON host
FOR EACH ROW EXECUTE PROCEDURE trg_host_insupdelbef();
SQL Fiddle con prova.
Utilizza un indice GIN nella colonna dell'array host.hostnames
e operatori di array per lavorarci:
- Perché il mio indice di matrice PostgreSQL non viene utilizzato (Rails 4)?
- Controlla se uno qualsiasi di un dato array di valori è presente in un array Postgres