Spravedlivá cesta
Možná budete chtít znovu zvážit normalizaci vaše schéma. Není nutné, aby se všichni "připojili i pro ten nejjednodušší dotaz" . Vytvořte VIEW
za to.
Tabulka může vypadat takto:
CREATE TABLE hostname (
hostname_id serial PRIMARY KEY
, host_id int REFERENCES host(host_id) ON UPDATE CASCADE ON DELETE CASCADE
, hostname text UNIQUE
);
Náhradní primární klíč hostname_id
je volitelné . Dávám přednost jednomu. Ve vašem případě hostname
může být primární klíč. Ale mnoho operací je rychlejších s jednoduchým malým integer
klíč. Vytvořte omezení cizího klíče pro propojení s tabulkou host
.
Vytvořte tento pohled:
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+
Počínaje str. 9.1 , primární klíč v GROUP BY
pokrývá všechny sloupce této tabulky v SELECT
seznam. Poznámky k verzi pro verzi 9.1:
Povolit jiné než
GROUP BY
sloupce v seznamu cílů dotazu, když je primární klíč zadán vGROUP BY
doložka
Dotazy mohou používat zobrazení jako tabulku. Hledání názvu hostitele bude moc rychleji tímto způsobem:
SELECT *
FROM host h
JOIN hostname hn USING (host_id)
WHERE hn.hostname = 'foobar';
V Postgres 9.2+ vícesloupcový index by byl ještě lepší, pokud můžete získat pouze indexové skenování z toho:
CREATE INDEX hn_multi_idx ON hostname (hostname, host_id);
Počínaje Postgres 9.3 , můžete použít MATERIALIZED VIEW
, okolnosti dovolí. Zvláště pokud mnohem častěji čtete, než píšete do tabulky.
Temná strana (na co jste se vlastně zeptali)
Pokud vás nemohu přesvědčit o spravedlivé cestě, pomohu i na temné straně. Jsem flexibilní. :)
Zde je ukázka, jak vynutit jedinečnost názvů hostitelů. Používám tabulku hostname
shromažďovat názvy hostitelů a spouštěč v tabulce host
aby byla aktuální. Jedinečná porušení vyvolávají výjimku a přerušují operaci.
CREATE TABLE host(hostnames text[]);
CREATE TABLE hostname(hostname text PRIMARY KEY); -- pk enforces uniqueness
Spouštěcí funkce:
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;
Spouštěč:
CREATE TRIGGER host_insupdelbef
BEFORE INSERT OR DELETE OR UPDATE OF hostnames ON host
FOR EACH ROW EXECUTE PROCEDURE trg_host_insupdelbef();
SQL Fiddle se zkušebním provozem.
Použijte index GIN ve sloupci pole host.hostnames
a operátory pole jak s ním pracovat:
- Proč se můj index pole PostgreSQL nepoužívá (Rails 4)?
- Zkontrolujte, zda je některá z daného pole hodnot přítomna v poli Postgres