PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Jak znaleźć tabele, które odwołują się do określonego wiersza za pomocą klucza obcego?

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 z information_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.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SQL :Utwórz pełny rekord z 2 tabel

  2. Algebraiczne typy danych w Postgres

  3. Uproszczenie zapytania z LIMIT w podzapytaniu i klauzulami WHERE zduplikowanymi w podzapytaniu i zapytaniu zewnętrznym

  4. Sqlalchemy.exc.OperationalError:(psycopg2.OperationalError)

  5. Postgres kolumna X nie istnieje