sql >> Base de Datos >  >> RDS >> PostgreSQL

¿Cómo puedo encontrar tablas que hagan referencia a una fila en particular a través de una clave externa?

Valores NULL en columnas de referencia

Esta consulta produce la instrucción DML para encontrar todas las filas en todas las tablas, donde una columna tiene una restricción de clave externa que hace referencia a otra tabla pero mantenga un NULL valor en esa columna:

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 consulta como esta:

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 una salida como esta:

    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
  • No cubre claves primarias o externas de varias columnas de manera confiable . Tienes que hacer la consulta más compleja para esto.

  • Lanzo todos los valores de clave principal a text para cubrir todo tipo.

  • Adapte o elimine estas líneas para encontrar una clave externa que apunte a otro o cualquiera columna / tabla:

    AND    c.confrelid = 'fk_tbl'::regclass
    AND    f.attname = 'fk_tbl_id' -- and only this column
    
  • Probado con PostgreSQL 9.1.4. Yo uso el pg_catalog mesas. Siendo realistas, nada de lo que uso aquí va a cambiar, pero eso no está garantizado en los principales lanzamientos. Vuelva a escribirlo con tablas de information_schema si necesita que funcione de manera confiable en todas las actualizaciones. Eso es más lento, pero seguro.

  • No desinfecté los nombres de las tablas en el script DML generado, porque quote_ident() fallaría con nombres calificados de esquema. Es su responsabilidad evitar nombres de tablas dañinos como "users; DELETE * FROM users;" . Con un poco más de esfuerzo, puede recuperar el nombre del esquema y el nombre de la tabla por separado y usar quote_ident() .

Valores NULL en columnas referenciadas

Mi primera solución hace algo sutilmente diferente de lo que pides, porque lo que describes (tal como lo entiendo) no existe. El valor NULL es "desconocido" y no puede ser referenciado. Si realmente desea encontrar filas con un NULL valor en una columna que tiene restricciones FK apuntando a (no a la fila en particular con el NULL valor, por supuesto), entonces la consulta se puede simplificar mucho:

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;

Encuentra todas esas filas en toda la base de datos (comentó la restricción a una tabla). Probado con Postgres 9.1.4 y funciona para mí.

Agrupo varias tablas que hacen referencia a la misma columna externa en una consulta y agrego una lista de tablas de referencia para brindar una descripción general.