sql >> Databáze >  >> RDS >> PostgreSQL

Může mít PostgreSQL omezení jedinečnosti na prvky pole?

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 v GROUP 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


  1. Najděte porušení cizího klíče v SQLite

  2. Nelze se připojit k Postgresql na portu 5432

  3. Jak vyvinout offline první nativní aplikaci pro Android

  4. Proč Postgres nepoužívá index?