Le droit chemin
Vous voudrez peut-être reconsidérer la normalisation votre schéma. Il n'est pas nécessaire que tout le monde "s'associe même pour la requête la plus simple" . Créer une VIEW
pour ça.
Le tableau pourrait ressembler à ceci :
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 clé primaire de substitution hostname_id
est facultatif . Je préfère en avoir un. Dans votre cas hostname
pourrait être la clé primaire. Mais de nombreuses opérations sont plus rapides avec un simple et petit integer
clé. Créer une contrainte de clé étrangère à lier à la table host
.
Créez une vue comme celle-ci :
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+
À partir de la page 9.1 , la clé primaire dans le GROUP BY
couvre toutes les colonnes de cette table dans le SELECT
liste. Les notes de version pour la version 9.1 :
Autoriser non-
GROUP BY
colonnes dans la liste cible de la requête lorsque la clé primaire est spécifiée dans leGROUP BY
clause
Les requêtes peuvent utiliser la vue comme une table. La recherche d'un nom d'hôte sera beaucoup plus vite de cette façon :
SELECT *
FROM host h
JOIN hostname hn USING (host_id)
WHERE hn.hostname = 'foobar';
Dans Postgres 9.2+ un index multicolonne serait encore mieux si vous pouviez obtenir une analyse d'index uniquement en sortir :
CREATE INDEX hn_multi_idx ON hostname (hostname, host_id);
À partir de Postgres 9.3 , vous pouvez utiliser une MATERIALIZED VIEW
, si les circonstances le permettent. Surtout si vous lisez beaucoup plus souvent que vous n'écrivez à la table.
Le côté obscur (ce que vous avez réellement demandé)
Si je ne peux pas vous convaincre du droit chemin, j'aiderai aussi du côté obscur. Je suis flexible. :)
Voici une démonstration sur la manière d'appliquer l'unicité des noms d'hôte. J'utilise une table hostname
pour collecter les noms d'hôtes et un trigger sur la table host
pour le tenir à jour. Les violations uniques déclenchent une exception et annulent l'opération.
CREATE TABLE host(hostnames text[]);
CREATE TABLE hostname(hostname text PRIMARY KEY); -- pk enforces uniqueness
Fonction déclencheur :
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;
Déclencheur :
CREATE TRIGGER host_insupdelbef
BEFORE INSERT OR DELETE OR UPDATE OF hostnames ON host
FOR EACH ROW EXECUTE PROCEDURE trg_host_insupdelbef();
Violon SQL avec essai.
Utiliser un indice GIN sur la colonne du tableau host.hostnames
et opérateurs de tableau pour travailler avec :
- Pourquoi mon index de tableau PostgreSQL n'est-il pas utilisé (Rails 4) ?
- Vérifier si l'une des valeurs d'un tableau donné est présente dans un tableau Postgres