PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Wie finde ich Tabellen, die über einen Fremdschlüssel auf eine bestimmte Zeile verweisen?

NULL-Werte in referenzierenden Spalten

Diese Abfrage erzeugt die DML-Anweisung, um alle Zeilen zu finden in allen Tabellen, in denen eine Spalte eine Fremdschlüsseleinschränkung auf eine andere Tabelle verweist hat aber halten Sie ein NULL Wert in dieser Spalte:

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;

Erzeugt eine Abfrage wie diese:

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;

Erzeugt eine Ausgabe wie diese:

    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
  • Überdeckt mehrspaltige Fremd- oder Primärschlüssel nicht zuverlässig . Dazu müssen Sie die Abfrage komplexer machen.

  • Ich caste alle Werte des Primärschlüssels zu text um alle Typen abzudecken.

  • Passen Sie diese Zeilen an oder entfernen Sie sie, um Fremdschlüssel zu finden, die auf einen anderen oder beliebigen zeigen Spalte / Tabelle:

    AND    c.confrelid = 'fk_tbl'::regclass
    AND    f.attname = 'fk_tbl_id' -- and only this column
    
  • Getestet mit PostgreSQL 9.1.4. Ich verwende den pg_catalog Tische. Realistischerweise wird sich nichts von dem, was ich hier verwende, ändern, aber das ist nicht für alle Hauptversionen garantiert. Schreiben Sie es mit Tabellen aus information_schema um wenn Sie es brauchen, um über Updates hinweg zuverlässig zu funktionieren. Das ist langsamer, aber sicher.

  • Ich habe Tabellennamen im generierten DML-Skript nicht bereinigt, weil quote_ident() würde mit Schema-qualifizierten Namen fehlschlagen. Es liegt in Ihrer Verantwortung, schädliche Tabellennamen wie "users; DELETE * FROM users;" zu vermeiden . Mit etwas mehr Aufwand können Sie Schemaname und Tabellenname separat abrufen und quote_ident() verwenden .

NULL-Werte in referenzierten Spalten

Meine erste Lösung unterscheidet sich subtil von dem, was Sie fragen, weil das, was Sie beschreiben (so wie ich es verstehe), nicht existiert. Der Wert NULL ist "unbekannt" und kann nicht referenziert werden. Wenn Sie tatsächlich Zeilen mit einem NULL finden möchten Wert in einer Spalte, die FK-Einschränkungen aufweist, die auf zeigen it (nicht auf die bestimmte Zeile mit dem NULL Wert natürlich), dann kann die Abfrage stark vereinfacht werden:

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;

Findet alle solche Zeilen in der gesamten Datenbank (Auskommentierung der Beschränkung auf eine Tabelle). Getestet mit Postgres 9.1.4 und funktioniert bei mir.

Ich gruppiere mehrere Tabellen, die auf dieselbe Fremdspalte verweisen, in einer Abfrage und füge eine Liste der verweisenden Tabellen hinzu, um einen Überblick zu geben.