sql >> Databáze >  >> RDS >> PostgreSQL

Jak najdu tabulky, které odkazují na konkrétní řádek pomocí cizího klíče?

Hodnoty NULL v odkazujících sloupcích

Tento dotaz vytvoří příkaz DML k nalezení všech řádků ve všech tabulkách, kde sloupec má omezení cizího klíče odkazující na jinou tabulku ale podržte NULL hodnota v tomto sloupci:

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;

Vytvoří dotaz, jako je tento:

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;

Produkuje výstup takto:

    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
  • Nepokrývá spolehlivě cizí nebo primární klíče s více sloupci . K tomu musíte udělat dotaz složitější.

  • Odesílám všechny hodnoty primárního klíče na text pokrývat všechny typy.

  • Upravte nebo odstraňte tyto řádky, abyste našli cizí klíč směřující na jiný nebo jakýkoli sloupec / tabulka:

    AND    c.confrelid = 'fk_tbl'::regclass
    AND    f.attname = 'fk_tbl_id' -- and only this column
    
  • Testováno s PostgreSQL 9.1.4. Používám pg_catalog tabulky. Realisticky se nic z toho, co zde používám, nezmění, ale to není zaručeno u hlavních vydání. Přepište jej pomocí tabulek z information_schema pokud potřebujete, aby fungovala spolehlivě napříč aktualizacemi. Je to pomalejší, ale jistě.

  • Nedezinfikoval jsem názvy tabulek ve vygenerovaném skriptu DML, protože quote_ident() selže s názvy kvalifikovanými pro schéma. Je vaší odpovědností vyhnout se škodlivým názvům tabulek jako "users; DELETE * FROM users;" . S trochou většího úsilí můžete načíst název schématu a název tabulky samostatně a použít quote_ident() .

Hodnoty NULL v odkazovaných sloupcích

Moje první řešení dělá něco jemně odlišného od toho, na co se ptáte, protože to, co popisujete (jak tomu rozumím), neexistuje. Hodnota NULL je "neznámý" a nelze na něj odkazovat. Pokud skutečně chcete najít řádky s NULL hodnotu ve sloupci, který má omezení FK směřující na to (ne na konkrétní řádek s NULL hodnota, samozřejmě), pak lze dotaz značně zjednodušit:

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;

Najde všechny takové řádky v celé databázi (komentováno omezení na jednu tabulku). Testováno s Postgres 9.1.4 a funguje pro mě.

Seskupuji více tabulek odkazujících na stejný cizí sloupec do jednoho dotazu a přidávám seznam odkazujících tabulek, abych poskytl přehled.



  1. mysql dotaz PHP:Chci, aby konkrétní položky byly první a mohu upravit dotaz, kolik položek se má zobrazit

  2. Použití vazebných proměnných s dynamickou klauzulí SELECT INTO v PL/SQL

  3. kombinování dotazů mysql AND OR v Codeigniter

  4. .NET Oracle spravované sdružování připojení k datům nefunguje nebo je pomalé