NULL-waarden in verwijzende kolommen
Deze query produceert de DML-instructie om alle rijen te vinden in alle tabellen, waar een kolom een externe-sleutelbeperking heeft verwijzend naar een andere tabel maar houd een NULL
. vast waarde in die kolom:
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;
Produceert een zoekopdracht als deze:
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;
Produceert output als volgt:
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
-
Dekt niet op betrouwbare wijze buitenlandse of primaire sleutels met meerdere kolommen . Hiervoor moet je de query ingewikkelder maken.
-
Ik cast alle primaire sleutel waarden naar
text
om alle soorten te dekken. -
Pas deze regels aan of verwijder ze om een refererende sleutel te vinden die naar een andere of een wijst kolom / tabel:
AND c.confrelid = 'fk_tbl'::regclass AND f.attname = 'fk_tbl_id' -- and only this column
-
Getest met PostgreSQL 9.1.4. Ik gebruik de
pg_catalog
tafels. Realistisch gezien zal er niets veranderen van wat ik hier gebruik, maar dat is niet gegarandeerd bij grote releases. Herschrijf het met tabellen uitinformation_schema
als u het nodig hebt om betrouwbaar te werken tussen updates. Dat is langzamer, maar zeker. -
Ik heb de tabelnamen in het gegenereerde DML-script niet opgeschoond, omdat
quote_ident()
zou mislukken met schema-gekwalificeerde namen. Het is uw verantwoordelijkheid om schadelijke tabelnamen zoals"users; DELETE * FROM users;"
te vermijden . Met wat meer moeite kunt u schemanaam en tabelnaam afzonderlijk ophalen enquote_ident()
gebruiken .
NULL-waarden in kolommen waarnaar wordt verwezen
Mijn eerste oplossing doet iets subtiel anders dan wat je vraagt, omdat wat je beschrijft (zoals ik het begrijp) niet bestaat. De waarde NULL
is "onbekend" en er kan niet naar worden verwezen. Als u rijen wilt vinden met een NULL
waarde in een kolom met FK-beperkingen die naar . verwijzen het (niet naar de specifieke rij met de NULL
waarde natuurlijk), dan kan de vraag veel vereenvoudigd worden:
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;
Vindt al dergelijke rijen in de hele database (commentaar op de beperking tot één tabel). Getest met Postgres 9.1.4 en werkt voor mij.
Ik groepeer meerdere tabellen die verwijzen naar dezelfde buitenlandse kolom in één zoekopdracht en voeg een lijst met referentietabellen toe om een overzicht te geven.