Wartości NULL w kolumnach odniesienia
To zapytanie generuje instrukcję DML, aby znaleźć wszystkie wiersze we wszystkich tabelach, w których kolumna ma ograniczenie klucza obcego odwołującego się do innej tabeli ale przytrzymaj NULL
wartość w tej kolumnie:
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;
Tworzy takie zapytanie:
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;
Tworzy dane wyjściowe w następujący sposób:
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
-
Nie obejmuje rzetelnie wielokolumnowych kluczy obcych lub kluczy podstawowych . W tym celu należy bardziej skomplikować zapytanie.
-
Przesyłam wszystkie wartości klucza podstawowego do
text
aby objąć wszystkie typy. -
Dostosuj lub usuń te wiersze, aby znaleźć klucz obcy wskazujący inny lub dowolny kolumna / tabela:
AND c.confrelid = 'fk_tbl'::regclass AND f.attname = 'fk_tbl_id' -- and only this column
-
Testowane z PostgreSQL 9.1.4. Używam
pg_catalog
tabele. Realistycznie nic z tego, czego tutaj używam, nie zmieni się, ale nie jest to gwarantowane w przypadku głównych wydań. Przepisz go tabelami zinformation_schema
jeśli potrzebujesz, aby działał niezawodnie w różnych aktualizacjach. To wolniej, ale na pewno. -
Nie oczyściłem nazw tabel w wygenerowanym skrypcie DML, ponieważ
quote_ident()
nie powiedzie się z nazwami kwalifikowanymi według schematu. Twoim obowiązkiem jest unikanie szkodliwych nazw tabel, takich jak"users; DELETE * FROM users;"
. Przy odrobinie wysiłku możesz osobno pobrać nazwę schematu i nazwę tabeli i użyćquote_ident()
.
Wartości NULL w odnośnych kolumnach
Moje pierwsze rozwiązanie robi coś nieco innego niż to, o co prosisz, ponieważ to, co opisujesz (tak jak rozumiem) nie istnieje. Wartość NULL
jest „nieznany” i nie można się do niego odwoływać. Jeśli rzeczywiście chcesz znaleźć wiersze z NULL
wartość w kolumnie, która ma ograniczenia FK wskazujące na go (nie do konkretnego wiersza z NULL
wartość oczywiście), wtedy zapytanie można znacznie uprościć:
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;
Znajduje wszystkie takie wiersze w całej bazie danych (zakomentowano ograniczenie do jednej tabeli). Testowane z Postgresem 9.1.4 i działa dla mnie.
Grupuję wiele tabel z odniesieniami do tej samej kolumny obcej w jedno zapytanie i dodaję listę tabel z odniesieniami, aby uzyskać przegląd.