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

Come posso trovare tabelle che fanno riferimento a una particolare riga tramite una chiave esterna?

Valori NULL nelle colonne di riferimento

Questa query produce l'istruzione DML per trovare tutte le righe in tutte le tabelle, dove una colonna ha un vincolo di chiave esterna che fa riferimento a un'altra tabella ma tieni un NULL valore in quella colonna:

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;

Produce una query come questa:

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;

Produce un output in questo modo:

    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
  • Non copre in modo affidabile chiavi esterne o primarie a più colonne . Devi rendere la query più complessa per questo.

  • Ho lanciato tutti i valori della chiave primaria a text per coprire tutti i tipi.

  • Adatta o rimuovi queste linee per trovare la chiave esterna che punta a un'altra o a qualsiasi colonna/tabella:

    AND    c.confrelid = 'fk_tbl'::regclass
    AND    f.attname = 'fk_tbl_id' -- and only this column
    
  • Testato con PostgreSQL 9.1.4. Uso il pg_catalog tavoli. Realisticamente nulla di ciò che uso qui cambierà, ma ciò non è garantito nelle versioni principali. Riscrivilo con le tabelle da information_schema se ne hai bisogno per funzionare in modo affidabile tra gli aggiornamenti. È più lento, ma certo.

  • Non ho disinfettato i nomi delle tabelle nello script DML generato, perché quote_ident() fallirebbe con nomi qualificati per lo schema. È tua responsabilità evitare nomi di tabelle dannosi come "users; DELETE * FROM users;" . Con un po' di sforzo in più, puoi recuperare il nome dello schema e il nome della tabella separatamente e utilizzare quote_ident() .

Valori NULL nelle colonne referenziate

La mia prima soluzione fa qualcosa di leggermente diverso da quello che chiedi, perché quello che descrivi (come lo capisco) è inesistente. Il valore NULL è "sconosciuto" e non può essere referenziato. Se vuoi effettivamente trovare righe con un NULL valore in una colonna con vincoli FK che puntano a a it (non alla riga particolare con NULL valore, ovviamente), quindi la query può essere molto semplificata:

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;

Trova tutte queste righe nell'intero database (ha commentato la restrizione su una tabella). Testato con Postgres 9.1.4 e funziona per me.

Raggruppo più tabelle che fanno riferimento alla stessa colonna esterna in una query e aggiungo un elenco di tabelle di riferimento per fornire una panoramica.