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 dainformation_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 utilizzarequote_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.