Valeurs NULL dans les colonnes de référence
Cette requête produit l'instruction DML pour rechercher toutes les lignes dans toutes les tables, où une colonne a une contrainte de clé étrangère référençant une autre table mais maintenez un NULL
valeur dans cette colonne :
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;
Produit une requête comme celle-ci :
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;
Produit une sortie comme celle-ci :
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
-
Ne couvre pas de manière fiable les clés étrangères ou primaires multicolonnes . Vous devez rendre la requête plus complexe pour cela.
-
Je convertis toutes les valeurs de clé primaire en
text
pour couvrir tous les types. -
Adaptez ou supprimez ces lignes pour trouver une clé étrangère pointant vers une autre ou any colonne / tableau :
AND c.confrelid = 'fk_tbl'::regclass AND f.attname = 'fk_tbl_id' -- and only this column
-
Testé avec PostgreSQL 9.1.4. J'utilise le
pg_catalog
les tables. De manière réaliste, rien de ce que j'utilise ici ne changera, mais cela n'est pas garanti dans les versions majeures. Réécrivez-le avec les tables deinformation_schema
si vous en avez besoin pour fonctionner de manière fiable entre les mises à jour. C'est plus lent, mais sûr. -
Je n'ai pas nettoyé les noms de table dans le script DML généré, car
quote_ident()
échouerait avec des noms qualifiés de schéma. Il est de votre responsabilité d'éviter les noms de table nuisibles comme"users; DELETE * FROM users;"
. Avec un peu plus d'effort, vous pouvez récupérer le nom du schéma et le nom de la table séparément et utiliserquote_ident()
.
Valeurs NULL dans les colonnes référencées
Ma première solution fait quelque chose de subtilement différent de ce que vous demandez, car ce que vous décrivez (tel que je le comprends) est inexistant. La valeur NULL
est "inconnu" et ne peut pas être référencé. Si vous voulez réellement trouver des lignes avec un NULL
valeur dans une colonne qui a des contraintes FK pointant vers il (pas à la ligne particulière avec le NULL
valeur, bien sûr), alors la requête peut être grandement simplifiée :
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;
Recherche toutes ces lignes dans l'ensemble de la base de données (a commenté la restriction à une table). Testé avec Postgres 9.1.4 et fonctionne pour moi.
Je regroupe plusieurs tables faisant référence à la même colonne étrangère dans une requête et j'ajoute une liste de tables de référence pour donner un aperçu.