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

NULL-værdier for referential_constraints.unique_constraint_*-kolonner i informationsskema

Test opsætning

Du antager begrænsningsnavnet test_def_abc_id_fkey , standardnavnet som følge af din opsætning i Postgres 11 eller ældre. Det er dog værd at bemærke, at standardnavne er blevet forbedret for Postgres 12, hvor den samme opsætning resulterer i test_def_abc_id_abc_id2_fkey . Udgivelsesbemærkningerne til Postgres 12:

Se:

db<>fiddle her

Så lad os bruge det eksplicitte navn test_def_abc_fkey for FK-begrænsningen for at undgå forvirring:

CREATE TABLE test_abc (
  pk  int PRIMARY KEY
, id  int NOT NULL
, id2 int NOT NULL
);

CREATE UNIQUE INDEX test_abc_ids ON test_abc(id,id2);

CREATE TABLE test_def (
  id      int PRIMARY KEY
, abc_id  int
, abc_id2 int
, CONSTRAINT test_def_abc_fkey  -- !
     FOREIGN KEY (abc_id,abc_id2) REFERENCES test_abc(id,id2)
);

Og at virker i Postgres 9.5 - Postgres 12.
Selv i Postgres 9.3.
(Jeg havde et forkert indtryk af en faktisk begrænsning ville være påkrævet.)

Svar

Din observation ved at forespørge informationsskemaet gælder:

SELECT *
FROM   information_schema.referential_constraints
WHERE  constraint_name = 'test_def_abc_fkey';  -- unequivocal name

Vi får en række, men de tre felter unique_constraint_catalog , unique_constraint_schema og unique_constraint_name er NULL .

Forklaringen virker simpel. Disse kolonner beskriver, som manualen udtrykker det:

Men der er ingen UNIQUE begrænsning , bare en UNIQUE indeks . En UNIQUE begrænsning er implementeret ved hjælp af en UNIQUE indeks i Postgres. Begrænsninger er defineret af SQL-standarden, indekser er implementeringsdetaljer. Der er forskelle som den, du opdagede. Relateret:

Den samme test med en faktisk UNIQUE begrænsning viser data som forventet:

db<>fiddle her

Så det ser ud til at give mening. Især siden informationsskemaet er også defineret af SQL-standardudvalget, og indekser er ikke standardiserede, kun begrænsninger. (Ingen indeksoplysninger i informationsskemavisninger.)

Fri bane? Ikke helt.

Men

Der er en anden informationsskemavisning key_column_usage . Dens sidste kolonne er beskrevet som:

Fed vægt min. Her er rækkefølgen af ​​kolonnen i indekset er angivet alligevel:

SELECT *
FROM   information_schema.key_column_usage
WHERE  constraint_name = 'test_def_abc_fkey';

Se:

db<>fiddle her

Virker inkonsekvent.

Hvad værre er, manualen hævder, at en faktisk PRIMÆR NØGLE eller UNIQUE begrænsning ville være påkrævet for at oprette en FREIGN KEY begrænsning:

Ser ud til at være en dokumentationsfejl ? Hvis ingen kan påpege, hvor jeg tager fejl her, indsender jeg en fejlrapport.

Relateret:

Løsning

I Postgres er systemkataloget den egentlige kilde til sandheden. Se:

Så du kunne bruge sådan noget (som jeg også tilføjede i violen ovenfor):

SELECT c.conname
     , c.conrelid::regclass  AS fk_table, k1.fk_columns
     , c.confrelid::regclass AS ref_table, k2.ref_key_columns
FROM   pg_catalog.pg_constraint c
LEFT   JOIN LATERAL (
   SELECT ARRAY (
      SELECT a.attname
      FROM   pg_catalog.pg_attribute a
           , unnest(c.conkey) WITH ORDINALITY AS k(attnum, ord)
      WHERE  a.attrelid = c.conrelid
      AND    a.attnum = k.attnum
      ORDER  BY k.ord
      ) AS fk_columns
   ) k1 ON true
LEFT   JOIN LATERAL (
   SELECT ARRAY (
      SELECT a.attname
      FROM   pg_catalog.pg_attribute a
           , unnest(c.confkey) WITH ORDINALITY AS k(attnum, ord)
      WHERE  a.attrelid = c.confrelid
      AND    a.attnum = k.attnum
      ORDER  BY k.ord
      ) AS ref_key_columns
   ) k2 ON true
WHERE  conname = 'test_def_abc_fkey';

Returnerer:

conname           | fk_table | fk_columns       | ref_table | ref_key_columns
:---------------- | :------- | :--------------- | :-------- | :--------------
test_def_abc_fkey | test_def | {abc_id,abc_id2} | test_abc  | {id,id2}       

Relateret:




  1. hvordan man refererer til en skemavariabel i plpgsql

  2. hvordan konverterer jeg tekst til jsonB

  3. Rails (postgres)-forespørgsel med jsonb-array

  4. Sådan returneres array, mens du bruger GROUP BY