sql >> Database >  >> RDS >> PostgreSQL

Kan PostgreSQL een uniciteitsbeperking hebben voor array-elementen?

Het rechtvaardige pad

Misschien wil je het normaliseren heroverwegen uw schema. Het is niet voor iedereen nodig om "mee te doen voor zelfs de eenvoudigste vraag" . Maak een VIEW daarvoor.

Tabel kan er als volgt uitzien:

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

De surrogaat primaire sleutel hostname_id is optioneel . Ik heb er liever een. In jouw geval hostname zou de primaire sleutel kunnen zijn. Maar veel bewerkingen zijn sneller met een eenvoudige, kleine integer sleutel. Maak een externe sleutelbeperking om te linken naar de tabel host .
Maak een weergave als deze:

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+

Beginnend met pg 9.1 , de primaire sleutel in de GROUP BY omvat alle kolommen van die tabel in de SELECT lijst. De release-opmerkingen voor versie 9.1:

Niet-GROUP BY Allow toestaan kolommen in de zoekdoellijst wanneer de primaire sleutel is opgegeven in de GROUP BY clausule

Query's kunnen de weergave gebruiken als een tabel. Zoeken naar een hostnaam is veel sneller op deze manier:

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

In Postgres 9.2+ een index met meerdere kolommen zou nog beter zijn als u een alleen-index scan . kunt krijgen eruit:

CREATE INDEX hn_multi_idx ON hostname (hostname, host_id);

Beginnend met Postgres 9.3 , kunt u een MATERIALIZED VIEW . gebruiken , omstandigheden het toelaten. Vooral als je veel vaker leest dan dat je naar de tafel schrijft.

De donkere kant (wat je eigenlijk vroeg)

Als ik je niet kan overtuigen van het rechtvaardige pad, zal ik ook aan de donkere kant helpen. Ik ben flexibel. :)

Hier is een demo hoe u de uniciteit van hostnamen kunt afdwingen. Ik gebruik een tabel hostname om hostnamen en een trigger op de tafel te verzamelen host om het up-to-date te houden. Unieke schendingen leiden tot een uitzondering en breken de bewerking af.

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

Trigger-functie:

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;

Trigger:

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

SQL Fiddle met proefdraaien.

Gebruik een GIN-index in de matrixkolom host.hostnames en matrixoperatoren om ermee te werken:

  • Waarom wordt mijn PostgreSQL-array-index niet gebruikt (Rails 4)?
  • Controleer of een van een bepaalde array van waarden aanwezig is in een Postgres-array


  1. Tabel draaien/draaien met aggregatie in Oracle

  2. Een overzicht van ProxySQL-clustering in ClusterControl

  3. Zorg voor PL/SQL-uitvoer in realtime

  4. Three table join met andere joins dan INNER JOIN