sql >> Databasteknik >  >> RDS >> PostgreSQL

Hur kan jag hitta tabeller som refererar till en viss rad via en främmande nyckel?

NULL-värden i refererande kolumner

Den här frågan producerar DML-satsen för att hitta alla rader i alla tabeller, där en kolumn har en begränsning med främmande nyckel som refererar till en annan tabell men håll en NULL värde i den kolumnen:

WITH x AS (
 SELECT c.conrelid::regclass    AS tbl
      , c.confrelid::regclass   AS ftbl
      , quote_ident(k.attname)  AS fk
      , quote_ident(pf.attname) AS pk
 FROM   pg_constraint c
 JOIN   pg_attribute  k ON (k.attrelid, k.attnum) = (c.conrelid, c.conkey[1])
 JOIN   pg_attribute  f ON (f.attrelid, f.attnum) = (c.confrelid, c.confkey[1])
 LEFT   JOIN pg_constraint p  ON p.conrelid = c.conrelid AND p.contype = 'p'
 LEFT   JOIN pg_attribute  pf ON (pf.attrelid, pf.attnum)
                               = (p.conrelid, p.conkey[1])
 WHERE  c.contype   = 'f'
 AND    c.confrelid = 'fk_tbl'::regclass  -- references to this tbl
 AND    f.attname   = 'fk_tbl_id'         -- and only to this column
)
SELECT string_agg(format(
'SELECT %L AS tbl
     , %L AS pk
     , %s::text AS pk_val
     , %L AS fk
     , %L AS ftbl
FROM   %1$s WHERE %4$s IS NULL'
                  , tbl
                  , COALESCE(pk 'NONE')
                  , COALESCE(pk 'NULL')
                  , fk
                  , ftbl), '
UNION ALL
') || ';'
FROM   x;

Ger en fråga som denna:

SELECT 'some_tbl' AS tbl
     , 'some_tbl_id' AS pk
     , some_tbl_id::text AS pk_val
     , 'fk_tbl_id' AS fk
     , 'fk_tbl' AS ftbl
FROM   some_tbl WHERE fk_tbl_id IS NULL
UNION ALL
SELECT 'other_tbl' AS tbl
     , 'other_tbl_id' AS pk
     , other_tbl_id::text AS pk_val
     , 'some_name_id' AS fk
     , 'fk_tbl' AS ftbl
FROM   other_tbl WHERE some_name_id IS NULL;

Ger utdata så här:

    tbl    |     pk       | pk_val |    fk        |  ftbl
-----------+--------------+--------+--------------+--------
 some_tbl  | some_tbl_id  | 49     | fk_tbl_id    | fk_tbl
 some_tbl  | some_tbl_id  | 58     | fk_tbl_id    | fk_tbl
 other_tbl | other_tbl_id | 66     | some_name_id | fk_tbl
 other_tbl | other_tbl_id | 67     | some_name_id | fk_tbl
  • Täcker inte främmande eller primärnycklar med flera kolumner på ett tillförlitligt sätt . Du måste göra frågan mer komplex för detta.

  • Jag castar alla primära nyckel-värden till text för att täcka alla typer.

  • Anpassa eller ta bort dessa rader för att hitta främmande nyckel som pekar på en annan eller vilken som helst kolumn/tabell:

    AND    c.confrelid = 'fk_tbl'::regclass
    AND    f.attname = 'fk_tbl_id' -- and only this column
    
  • Testad med PostgreSQL 9.1.4. Jag använder pg_catalog tabeller. Realistiskt sett kommer ingenting av det jag använder här att förändras, men det är inte garanterat över större utgåvor. Skriv om det med tabeller från information_schema om du behöver det för att fungera tillförlitligt över uppdateringar. Det är långsammare, men säkert.

  • Jag sanerade inte tabellnamn i det genererade DML-skriptet eftersom quote_ident() skulle misslyckas med schemakvalificerade namn. Det är ditt ansvar att undvika skadliga tabellnamn som "users; DELETE * FROM users;" . Med lite mer ansträngning kan du hämta schemanamn och tabellnamn separat och använda quote_ident() .

NULL-värden i refererade kolumner

Min första lösning gör något subtilt annorlunda än vad du frågar om, eftersom det du beskriver (som jag förstår det) är obefintligt. Värdet NULL är "okänt" och kan inte refereras. Om du verkligen vill hitta rader med en NULL värde i en kolumn som har FK-begränsningar som pekar till den (inte till den specifika raden med NULL värde, naturligtvis), då kan frågan förenklas mycket:

WITH x AS (
 SELECT c.confrelid::regclass   AS ftbl
       ,quote_ident(f.attname)  AS fk
       ,quote_ident(pf.attname) AS pk
       ,string_agg(c.conrelid::regclass::text, ', ') AS referencing_tbls
 FROM   pg_constraint c
 JOIN   pg_attribute  f ON (f.attrelid, f.attnum) = (c.confrelid, c.confkey[1])
 LEFT   JOIN pg_constraint p  ON p.conrelid = c.confrelid AND p.contype = 'p'
 LEFT   JOIN pg_attribute  pf ON (pf.attrelid, pf.attnum)
                               = (p.conrelid, p.conkey[1])
 WHERE  c.contype = 'f'
 -- AND    c.confrelid = 'fk_tbl'::regclass  -- only referring this tbl
 GROUP  BY 1, 2, 3
)
SELECT string_agg(format(
'SELECT %L AS ftbl
     , %L AS pk
     , %s::text AS pk_val
     , %L AS fk
     , %L AS referencing_tbls
FROM   %1$s WHERE %4$s IS NULL'
                  , ftbl
                  , COALESCE(pk, 'NONE')
                  , COALESCE(pk, 'NULL')
                  , fk
                  , referencing_tbls), '
UNION ALL
') || ';'
FROM   x;

Hittar alla sådana rader i hela databasen (kommenterade ut begränsningen till en tabell). Testat med Postgres 9.1.4 och fungerar för mig.

Jag grupperar flera tabeller som refererar till samma främmande kolumn i en fråga och lägger till en lista med referenstabeller för att ge en översikt.



  1. Skillnad mellan MySQL-terminologi och främmande nycklar?

  2. Databasserverhårdtrender

  3. Bästa sättet att lagra data på serversidan?

  4. Oracle:SQL välj datum med tidsstämpel