sql >> Databasteknik >  >> RDS >> PostgreSQL

Kan PostgreSQL ha en unik begränsning för arrayelement?

Den rättfärdiga vägen

Du kanske vill ompröva normalisering ditt schema. Det är inte nödvändigt för alla att "gå med för ens den enklaste frågan" . Skapa en VIEW för det.

Tabell kan se ut så här:

CREATE TABLE hostname (
  hostname_id serial PRIMARY KEY
, host_id     int  REFERENCES host(host_id) ON UPDATE CASCADE ON DELETE CASCADE
, hostname    text UNIQUE
);

Den primära surrogatnyckeln hostname_id är valfritt . Jag föredrar att ha en. I ditt fall hostname kan vara den primära nyckeln. Men många operationer är snabbare med ett enkelt, litet integer nyckel. Skapa en främmande nyckel-begränsning för att länka till tabellen host .
Skapa en vy så här:

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+

Börjar med sidan 9.1 , den primära nyckeln i GROUP BY täcker alla kolumner i den tabellen i SELECT lista. Utgivningsinformationen för version 9.1:

Tillåt icke-GROUP BY kolumner i frågemållistan när primärnyckeln anges i GROUP BY klausul

Frågor kan använda vyn som en tabell. Att söka efter ett värdnamn blir mycket snabbare så här:

SELECT *
FROM   host h
JOIN   hostname hn USING (host_id)
WHERE  hn.hostname = 'foobar';

I Postgres 9.2+ ett index med flera kolumner skulle vara ännu bättre om du kan få en endast indexsökning ur det:

CREATE INDEX hn_multi_idx ON hostname (hostname, host_id);

Börjar med Postgres 9.3 , kan du använda en MATERIALIZED VIEW , om omständigheterna tillåter. Speciellt om du läser mycket oftare än du skriver till bordet.

Den mörka sidan (det du faktiskt frågade om)

Om jag inte kan övertyga dig om den rättfärdiga vägen, hjälper jag också till på den mörka sidan. Jag är flexibel. :)

Här är en demonstration av hur man upprätthåller unika värdnamn. Jag använder en tabell hostname för att samla in värdnamn och en trigger på tabellen host för att hålla den uppdaterad. Unika överträdelser skapar ett undantag och avbryter operationen.

CREATE TABLE host(hostnames text[]);
CREATE TABLE hostname(hostname text PRIMARY KEY);  --  pk enforces uniqueness

Triggerfunktion:

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;

Utlösare:

CREATE TRIGGER host_insupdelbef
BEFORE INSERT OR DELETE OR UPDATE OF hostnames ON host
FOR EACH ROW EXECUTE PROCEDURE trg_host_insupdelbef();

SQL Fiddle med provkörning.

Använd ett GIN-index i arraykolumnen host.hostnames och matrisoperatorer att arbeta med det:

  • Varför används inte mitt PostgreSQL-arrayindex (Rails 4)?
  • Kontrollera om någon av en given matris med värden finns i en Postgres-matris


  1. Synkronisera offline SQLite databas med online MySQL databas

  2. Hur man får det aktuella datumet i MySQL

  3. Skapa en vy i SQL Server 2017

  4. Hibernate, Postgres &Array Type