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 deinformation_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 usarquote_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.