sql >> Database teknologi >  >> RDS >> PostgreSQL

Kan PostgreSQL have en unikhedsbegrænsning på array-elementer?

Den retfærdige vej

Du vil måske genoverveje normalisering dit skema. Det er ikke nødvendigt for alle at "deltage for selv den enkleste forespørgsel" . Opret en VIEW for det.

Tabellen kunne se sådan ud:

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ære surrogatnøgle hostname_id er valgfri . Jeg foretrækker at have en. I dit tilfælde hostname kunne være den primære nøgle. Men mange operationer er hurtigere med et simpelt, lille integer nøgle. Opret en fremmednøglebegrænsning for at linke til tabellen host .
Opret en visning som denne:

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+

Starter med side 9.1 , den primære nøgle i GROUP BY dækker alle kolonner i den tabel i SELECT liste. Udgivelsesbemærkningerne til version 9.1:

Tillad ikke-GROUP BY kolonner i forespørgselsmållisten, når primærnøglen er angivet i GROUP BY klausul

Forespørgsler kan bruge visningen som en tabel. At søge efter et værtsnavn vil være meget hurtigere på denne måde:

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

I Postgres 9.2+ et indeks med flere kolonner ville være endnu bedre, hvis du kan få en kun-indeksscanning ud af det:

CREATE INDEX hn_multi_idx ON hostname (hostname, host_id);

Starter med Postgres 9.3 , kan du bruge en MATERIALIZED VIEW , hvis omstændighederne tillader det. Især hvis du læser meget oftere, end du skriver til bordet.

Den mørke side (det du faktisk spurgte om)

Hvis jeg ikke kan overbevise dig om den retfærdige vej, vil jeg også hjælpe på den mørke side. Jeg er fleksibel. :)

Her er en demo, hvordan man håndhæver unikke værtsnavne. Jeg bruger en tabel hostname at indsamle værtsnavne og en trigger på tabellen host for at holde det opdateret. Unikke overtrædelser rejser en undtagelse og afbryde operationen.

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

Udløserfunktion:

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;

Udløser:

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 testkørsel.

Brug et GIN-indeks i array-kolonnen host.hostnames og array-operatorer at arbejde med det:

  • Hvorfor bliver mit PostgreSQL-array-indeks ikke brugt (Rails 4)?
  • Tjek, om nogen af ​​en given matrix af værdier er til stede i en Postgres-array


  1. SQL-forespørgsler

  2. Selvstudium til SQL Server-tabelopdeling og -partitioner

  3. Brug af avancerede Oracle JDeveloper-funktioner til MySQL-databaser

  4. Forbindelsestimeout for DriverManager getConnection