NULL-værdier i refererende kolonner
Denne forespørgsel producerer DML-sætningen for at finde alle rækker i alle tabeller, hvor en kolonne har en fremmednøglebegrænsning der henviser til en anden tabel men hold en NULL
værdi i den kolonne:
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;
Frembringer en forespørgsel som denne:
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;
Producerer output som dette:
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
-
Dækker ikke udenlandske eller primære nøgler med flere kolonner på en pålidelig måde . Du skal gøre forespørgslen mere kompleks for dette.
-
Jeg caster alle primære nøgle værdier til
text
til at dække alle typer. -
Tilpas eller fjern disse linjer for at finde fremmednøgle, der peger på en anden eller enhver kolonne / tabel:
AND c.confrelid = 'fk_tbl'::regclass AND f.attname = 'fk_tbl_id' -- and only this column
-
Testet med PostgreSQL 9.1.4. Jeg bruger
pg_catalog
borde. Realistisk set vil intet af det, jeg bruger her, ændre sig, men det er ikke garanteret på tværs af større udgivelser. Omskriv det med tabeller frainformation_schema
hvis du har brug for det til at fungere pålideligt på tværs af opdateringer. Det er langsommere, men sikkert. -
Jeg rensede ikke tabelnavne i det genererede DML-script, fordi
quote_ident()
ville mislykkes med skema-kvalificerede navne. Det er dit ansvar at undgå skadelige tabelnavne som"users; DELETE * FROM users;"
. Med lidt mere indsats kan du hente skemanavn og tabelnavn separat og brugequote_ident()
.
NULL-værdier i refererede kolonner
Min første løsning gør noget subtilt anderledes end det du spørger om, for det du beskriver (som jeg forstår det) er ikke-eksisterende. Værdien NULL
er "ukendt" og kan ikke refereres. Hvis du rent faktisk ønsker at finde rækker med en NULL
værdi i en kolonne, der har FK-begrænsninger, der peger til det (ikke til den bestemte række med NULL
værdi, selvfølgelig), så kan forespørgslen være meget forenklet:
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;
Finder alle sådanne rækker i hele databasen (kommenterede begrænsningen til én tabel). Testet med Postgres 9.1.4 og virker for mig.
Jeg grupperer flere tabeller, der refererer til den samme fremmede kolonne, i én forespørgsel og tilføjer en liste over referencetabeller for at give et overblik.