sql >> Databasteknik >  >> RDS >> PostgreSQL

NULL-värden för referential_constraints.unique_constraint_*-kolumner i informationsschemat

Testa installationen

Du antar begränsningsnamnet test_def_abc_id_fkey , standardnamnet som härrör från din installation i Postgres 11 eller äldre. Värt att notera är dock att standardnamnen har förbättrats för Postgres 12, där samma inställning resulterar i test_def_abc_id_abc_id2_fkey . Versionsinformationen för Postgres 12:

Se:

db<>fiol här

Så låt oss använda det explicita namnet test_def_abc_fkey för FK-begränsningen för att undvika förvirring:

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)
);

Och att fungerar i Postgres 9.5 - Postgres 12.
Även i Postgres 9.3.
(Jag hade ett felaktigt intryck av en faktisk begränsning skulle krävas.)

Svar

Din observation från att fråga informationsschemat gäller:

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

Vi får en rad, men de tre fälten unique_constraint_catalog , unique_constraint_schema och unique_constraint_name är NULL .

Förklaringen verkar enkel. Dessa kolumner beskriver, som manualen uttrycker det:

Men det finns ingen UNIQUE begränsning , bara en UNIQUE index . En UNIQUE begränsning implementeras med en UNIQUE index i Postgres. Restriktioner definieras av SQL-standarden, index är implementeringsdetaljer. Det finns skillnader som den du upptäckte. Relaterat:

Samma test med en faktisk UNIQUE begränsning visar data som förväntat:

db<>fiol här

Så det här verkar vara vettigt. Speciellt eftersom informationsschemat definieras också av SQL-standardkommittén och index är inte standardiserade, bara begränsningar. (Ingen indexinformation i informationsschemavyer.)

Allt klart? Inte riktigt.

Men

Det finns en annan informationsschemavy key_column_usage . Dess sista kolumn beskrivs som:

Fet betoning min. Här, ordningspositionen för kolumnen i index finns i alla fall:

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

Se:

db<>fiol här

Verkar inkonsekvent.

Vad värre är, handboken hävdar att en faktisk PRIMÄRNYCKEL eller UNIQUE begränsning skulle krävas för att skapa en FREIGN KEY begränsning:

Verkar vara ett dokumentationsfel ? Om ingen kan peka ut var jag gör fel här, skickar jag en felrapport.

Relaterat:

Lösning

I Postgres är systemkatalogen den faktiska källan till sanningen. Se:

Så du kan använda något sånt här (som jag också la till i fiol ovan):

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';

Returnerar:

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

Relaterat:




  1. Finns det sätt att ge ett användarvänligt felmeddelande vid överträdelse av begränsningar

  2. Visa artiklar från en wordpress-sajt på en icke-wordpress-sajt

  3. Infoga aktuellt datum/tid med now() i ett fält med MySQL/PHP

  4. ORDER BY ignoreras när du infogar i MySQL-tabellen