Valores NULL nas colunas de referência
Essa consulta produz a instrução DML para localizar todas as linhas em todas as tabelas, onde uma coluna tem uma restrição de chave estrangeira referenciando outra tabela mas mantenha um
NULL
valor nessa coluna: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;
Produz uma 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;
Produz saída 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
-
Não cobre chaves estrangeiras ou primárias de várias colunas de forma confiável . Você tem que tornar a consulta mais complexa para isso.
-
Eu converto todos os valores de chave primária paratext
para cobrir todos os tipos.
-
Adapte ou remova essas linhas para encontrar uma chave estrangeira apontando para outra ou qualquer coluna/tabela:
AND c.confrelid = 'fk_tbl'::regclass AND f.attname = 'fk_tbl_id' -- and only this column
-
Testado com PostgreSQL 9.1.4. Eu uso opg_catalog
mesas. Realisticamente, nada do que eu uso aqui vai mudar, mas isso não é garantido nos principais lançamentos. Reescreva-o com tabelas deinformation_schema
se você precisar que ele funcione de maneira confiável nas atualizações. Isso é mais lento, mas com certeza.
-
Não limpei os nomes das tabelas no script DML gerado porquequote_ident()
falharia com nomes qualificados pelo esquema. É sua responsabilidade evitar nomes de tabelas prejudiciais como"users; DELETE * FROM users;"
. Com um pouco mais de esforço, você pode recuperar o nome do esquema e o nome da tabela separadamente e usarquote_ident()
.
Valores NULL nas colunas referenciadas
Minha primeira solução faz algo sutilmente diferente do que você pergunta, porque o que você descreve (como eu o entendo) é inexistente. O valor
NULL
é "desconhecido" e não pode ser referenciado. Se você realmente deseja encontrar linhas com um NULL
valor em uma coluna que tem restrições FK apontando para (não para a linha específica com o NULL
valor, é claro), então a consulta pode ser muito simplificada: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;
Localiza todas essas linhas em todo o banco de dados (comentou a restrição a uma tabela). Testado com Postgres 9.1.4 e funciona para mim.
Agrupo várias tabelas que fazem referência à mesma coluna estrangeira em uma consulta e adiciono uma lista de tabelas de referência para fornecer uma visão geral.