it-swarm.com.de

Postgres: SQL, um Fremdschlüssel der Tabelle aufzulisten

Gibt es eine Möglichkeit, mithilfe von SQL alle Fremdschlüssel für eine bestimmte Tabelle aufzulisten? Ich kenne den Tabellennamen/das Schema und kann das anschließen.

164
smack0007

Sie können dies über die Tabellen information_schema tun. Zum Beispiel:

SELECT
    tc.table_schema, 
    tc.constraint_name, 
    tc.table_name, 
    kcu.column_name, 
    ccu.table_schema AS foreign_table_schema,
    ccu.table_name AS foreign_table_name,
    ccu.column_name AS foreign_column_name 
FROM 
    information_schema.table_constraints AS tc 
    JOIN information_schema.key_column_usage AS kcu
      ON tc.constraint_name = kcu.constraint_name
      AND tc.table_schema = kcu.table_schema
    JOIN information_schema.constraint_column_usage AS ccu
      ON ccu.constraint_name = tc.constraint_name
      AND ccu.table_schema = tc.table_schema
WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_name='mytable';
289
ollyc

psql macht dies, und wenn Sie psql mit starten:

psql -E

es zeigt Ihnen genau, welche Abfrage ausgeführt wird. Bei der Suche nach Fremdschlüsseln gilt:

SELECT conname,
  pg_catalog.pg_get_constraintdef(r.oid, true) as condef
FROM pg_catalog.pg_constraint r
WHERE r.conrelid = '16485' AND r.contype = 'f' ORDER BY 1

In diesem Fall ist 16485 die OID der Tabelle, die ich mir anschaue - Sie können diese erhalten, indem Sie einfach Ihren Tabellennamen wie folgt regeln:

WHERE r.conrelid = 'mytable'::regclass

Schema-qualifizieren Sie den Tabellennamen, wenn er nicht eindeutig ist (oder der erste in Ihrem search_path):

WHERE r.conrelid = 'myschema.mytable'::regclass
56
Magnus Hagander

Ollycs Antwort ist gut, da sie nicht Postgres-spezifisch ist. Sie bricht jedoch zusammen, wenn der Fremdschlüssel auf mehrere Spalten verweist. Die folgende Abfrage funktioniert für eine beliebige Anzahl von Spalten, hängt jedoch stark von Postgres-Erweiterungen ab:

select 
    att2.attname as "child_column", 
    cl.relname as "parent_table", 
    att.attname as "parent_column",
    conname
from
   (select 
        unnest(con1.conkey) as "parent", 
        unnest(con1.confkey) as "child", 
        con1.confrelid, 
        con1.conrelid,
        con1.conname
    from 
        pg_class cl
        join pg_namespace ns on cl.relnamespace = ns.oid
        join pg_constraint con1 on con1.conrelid = cl.oid
    where
        cl.relname = 'child_table'
        and ns.nspname = 'child_schema'
        and con1.contype = 'f'
   ) con
   join pg_attribute att on
       att.attrelid = con.confrelid and att.attnum = con.child
   join pg_class cl on
       cl.oid = con.confrelid
   join pg_attribute att2 on
       att2.attrelid = con.conrelid and att2.attnum = con.parent
38
martin

Ausgabe \d+ tablename in PostgreSQL Prompt. Zusätzlich zum Anzeigen der Datentypen der Tabellenspalten werden die Indizes und Fremdschlüssel angezeigt.

24
Gre Hahn

Erweiterung zum Ollyc-Rezept:

CREATE VIEW foreign_keys_view AS
SELECT
    tc.table_name, kcu.column_name,
    ccu.table_name AS foreign_table_name,
    ccu.column_name AS foreign_column_name
FROM
    information_schema.table_constraints AS tc
    JOIN information_schema.key_column_usage 
        AS kcu ON tc.constraint_name = kcu.constraint_name
    JOIN information_schema.constraint_column_usage 
        AS ccu ON ccu.constraint_name = tc.constraint_name
WHERE constraint_type = 'FOREIGN KEY';

Dann:

SELECT * FROM foreign_keys_view WHERE table_name='YourTableNameHere';

24
Mvoicem

Überprüfen Sie die ff-Post für Ihre Lösung und vergessen Sie nicht, dies zu markieren, wenn Sie dies als hilfreich erachten

http://errorbank.blogspot.com/2011/03/list-all-foreign-keys-references-for.html

SELECT
  o.conname AS constraint_name,
  (SELECT nspname FROM pg_namespace WHERE oid=m.relnamespace) AS source_schema,
  m.relname AS source_table,
  (SELECT a.attname FROM pg_attribute a WHERE a.attrelid = m.oid AND a.attnum = o.conkey[1] AND a.attisdropped = false) AS source_column,
  (SELECT nspname FROM pg_namespace WHERE oid=f.relnamespace) AS target_schema,
  f.relname AS target_table,
  (SELECT a.attname FROM pg_attribute a WHERE a.attrelid = f.oid AND a.attnum = o.confkey[1] AND a.attisdropped = false) AS target_column
FROM
  pg_constraint o LEFT JOIN pg_class f ON f.oid = o.confrelid LEFT JOIN pg_class m ON m.oid = o.conrelid
WHERE
  o.contype = 'f' AND o.conrelid IN (SELECT oid FROM pg_class c WHERE c.relkind = 'r');
12
sheldon

Ich denke, was Sie gesucht haben und sehr nahe an dem, was @ollyc schrieb, ist:

SELECT
tc.constraint_name, tc.table_name, kcu.column_name, 
ccu.table_name AS foreign_table_name,
ccu.column_name AS foreign_column_name 
FROM 
information_schema.table_constraints AS tc 
JOIN information_schema.key_column_usage AS kcu
  ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage AS ccu
  ON ccu.constraint_name = tc.constraint_name
WHERE constraint_type = 'FOREIGN KEY' AND ccu.table_name='YourTableNameHere';

Dadurch werden alle Tabellen aufgelistet, die Ihre angegebene Tabelle als Fremdschlüssel verwenden

9
Shaun McCready

Diese Abfrage funktioniert auch bei zusammengesetzten Schlüsseln korrekt:

select c.constraint_name
    , x.table_schema as schema_name
    , x.table_name
    , x.column_name
    , y.table_schema as foreign_schema_name
    , y.table_name as foreign_table_name
    , y.column_name as foreign_column_name
from information_schema.referential_constraints c
join information_schema.key_column_usage x
    on x.constraint_name = c.constraint_name
join information_schema.key_column_usage y
    on y.ordinal_position = x.position_in_unique_constraint
    and y.constraint_name = c.unique_constraint_name
order by c.constraint_name, x.ordinal_position
8
oscavi

Sie können die PostgreSQL-Systemkataloge verwenden. Vielleicht können Sie pg_constraint abfragen, um Fremdschlüssel anzufordern Sie können auch das Informationsschema verwenden.

5
Guido

Um Martins hervorragende Antwort zu erläutern, handelt es sich hier um eine Abfrage, mit der Sie anhand der übergeordneten Tabelle filtern und Ihnen den Namen der untergeordneten Tabelle mit jeder übergeordneten Tabelle anzeigen können, sodass Sie alle abhängigen Tabellen/Spalten basierend auf den Fremdschlüsseleinschränkungen in sehen können die übergeordnete Tabelle.

select 
    con.constraint_name,
    att2.attname as "child_column", 
    cl.relname as "parent_table", 
    att.attname as "parent_column",
    con.child_table,
    con.child_schema
from
   (select 
        unnest(con1.conkey) as "parent", 
        unnest(con1.confkey) as "child", 
        con1.conname as constraint_name,
        con1.confrelid, 
        con1.conrelid,
        cl.relname as child_table,
        ns.nspname as child_schema
    from 
        pg_class cl
        join pg_namespace ns on cl.relnamespace = ns.oid
        join pg_constraint con1 on con1.conrelid = cl.oid
    where  con1.contype = 'f'
   ) con
   join pg_attribute att on
       att.attrelid = con.confrelid and att.attnum = con.child
   join pg_class cl on
       cl.oid = con.confrelid
   join pg_attribute att2 on
       att2.attrelid = con.conrelid and att2.attnum = con.parent
   where cl.relname like '%parent_table%'       
4
Cervo

Hier ist eine Lösung von Andreas Joseph Krogh aus der PostgreSQL-Mailingliste: http://www.postgresql.org/message-id/[email protected]

SELECT source_table::regclass, source_attr.attname AS source_column,
    target_table::regclass, target_attr.attname AS target_column
FROM pg_attribute target_attr, pg_attribute source_attr,
  (SELECT source_table, target_table, source_constraints[i] source_constraints, target_constraints[i] AS target_constraints
   FROM
     (SELECT conrelid as source_table, confrelid AS target_table, conkey AS source_constraints, confkey AS target_constraints,
       generate_series(1, array_upper(conkey, 1)) AS i
      FROM pg_constraint
      WHERE contype = 'f'
     ) query1
  ) query2
WHERE target_attr.attnum = target_constraints AND target_attr.attrelid = target_table AND
      source_attr.attnum = source_constraints AND source_attr.attrelid = source_table;

Diese Lösung verarbeitet Fremdschlüssel, die auf mehrere Spalten verweisen, und vermeidet Duplikate (was bei einigen anderen Antworten nicht der Fall ist). Das einzige, was ich geändert habe, waren die Variablennamen.

Hier ist ein Beispiel, das alle employee-Spalten zurückgibt, die auf die permission-Tabelle verweisen:

SELECT source_column
FROM foreign_keys
WHERE source_table = 'employee'::regclass AND target_table = 'permission'::regclass;
4
Gili

Verwenden Sie den Namen des Primärschlüssels, auf den die Schlüssel verweisen, und fragen Sie das information_schema ab:

select table_name, column_name
from information_schema.key_column_usage
where constraint_name IN (select constraint_name
  from information_schema.referential_constraints 
  where unique_constraint_name = 'TABLE_NAME_pkey')

Hier ist 'TABLE_NAME_pkey' der Name des Primärschlüssels, auf den die Fremdschlüssel verweisen.

4
markmnl

Keine der vorhandenen Antworten gab mir Ergebnisse in der Form, in der ich sie eigentlich wollte. Hier also meine (gigantische) Abfrage nach Informationen über Fremdschlüssel.

Ein paar Anmerkungen:

  • Die Ausdrücke, die zum Generieren von from_cols und to_cols verwendet wurden, konnten in Postgres 9.4 und später mit WITH ORDINALITY erheblich vereinfacht werden, statt mit dem von mir verwendeten Hackery-Funktionstool.
  • Dieselben Ausdrücke verlassen sich darauf, dass der Abfrageplaner die zurückgegebene Reihenfolge der Ergebnisse von UNNEST nicht ändert. Ich glaube nicht, dass dies der Fall sein wird, aber ich habe in meinem Datensatz keine mehrspaltigen Fremdschlüssel zum Testen. Durch das Hinzufügen der 9,4-Feinheiten entfällt diese Möglichkeit insgesamt.
  • Die Abfrage selbst erfordert Postgres 9.0 oder höher (8.x hat ORDER BY in Aggregatfunktionen nicht zugelassen)
  • Ersetzen Sie STRING_AGG durch ARRAY_AGG, wenn Sie anstelle von durch Kommas getrennten Zeichenfolgen ein Array von Spalten wünschen.

-

SELECT
    c.conname AS constraint_name,
    (SELECT n.nspname FROM pg_namespace AS n WHERE n.oid=c.connamespace) AS constraint_schema,

    tf.name AS from_table,
    (
        SELECT STRING_AGG(QUOTE_IDENT(a.attname), ', ' ORDER BY t.seq)
        FROM
            (
                SELECT
                    ROW_NUMBER() OVER (ROWS UNBOUNDED PRECEDING) AS seq,
                    attnum
                FROM
                    UNNEST(c.conkey) AS t(attnum)
            ) AS t
            INNER JOIN pg_attribute AS a ON a.attrelid=c.conrelid AND a.attnum=t.attnum
    ) AS from_cols,

    tt.name AS to_table,
    (
        SELECT STRING_AGG(QUOTE_IDENT(a.attname), ', ' ORDER BY t.seq)
        FROM
            (
                SELECT
                    ROW_NUMBER() OVER (ROWS UNBOUNDED PRECEDING) AS seq,
                    attnum
                FROM
                    UNNEST(c.confkey) AS t(attnum)
            ) AS t
            INNER JOIN pg_attribute AS a ON a.attrelid=c.confrelid AND a.attnum=t.attnum
    ) AS to_cols,

    CASE confupdtype WHEN 'r' THEN 'restrict' WHEN 'c' THEN 'cascade' WHEN 'n' THEN 'set null' WHEN 'd' THEN 'set default' WHEN 'a' THEN 'no action' ELSE NULL END AS on_update,
    CASE confdeltype WHEN 'r' THEN 'restrict' WHEN 'c' THEN 'cascade' WHEN 'n' THEN 'set null' WHEN 'd' THEN 'set default' WHEN 'a' THEN 'no action' ELSE NULL END AS on_delete,
    CASE confmatchtype::text WHEN 'f' THEN 'full' WHEN 'p' THEN 'partial' WHEN 'u' THEN 'simple' WHEN 's' THEN 'simple' ELSE NULL END AS match_type,  -- In earlier postgres docs, simple was 'u'nspecified, but current versions use 's'imple.  text cast is required.

    pg_catalog.pg_get_constraintdef(c.oid, true) as condef
FROM
    pg_catalog.pg_constraint AS c
    INNER JOIN (
        SELECT pg_class.oid, QUOTE_IDENT(pg_namespace.nspname) || '.' || QUOTE_IDENT(pg_class.relname) AS name
        FROM pg_class INNER JOIN pg_namespace ON pg_class.relnamespace=pg_namespace.oid
    ) AS tf ON tf.oid=c.conrelid
    INNER JOIN (
        SELECT pg_class.oid, QUOTE_IDENT(pg_namespace.nspname) || '.' || QUOTE_IDENT(pg_class.relname) AS name
        FROM pg_class INNER JOIN pg_namespace ON pg_class.relnamespace=pg_namespace.oid
    ) AS tt ON tt.oid=c.confrelid
WHERE c.contype = 'f' ORDER BY 1;
3
dewin
SELECT r.conname
      ,ct.table_name
      ,pg_catalog.pg_get_constraintdef(r.oid, true) as condef
  FROM pg_catalog.pg_constraint r, information_schema.constraint_table_usage ct
 WHERE r.contype = 'f' 
   AND r.conname = ct.constraint_name
 ORDER BY 1

Die richtige Lösung für das Problem, indem Sie information_schema verwenden, mit mehrspaltigen Schlüsseln arbeiten, Spalten verschiedener Namen in beiden Tabellen korrekt verknüpfen und auch mit ms sqlsever kompatibel sind:

select fks.TABLE_NAME as foreign_key_table_name
, fks.CONSTRAINT_NAME as foreign_key_constraint_name
, kcu_foreign.COLUMN_NAME as foreign_key_column_name
, rc.UNIQUE_CONSTRAINT_NAME as primary_key_constraint_name
, pks.TABLE_NAME as primary_key_table_name
, kcu_primary.COLUMN_NAME as primary_key_column_name
from INFORMATION_SCHEMA.TABLE_CONSTRAINTS fks -- foreign keys
inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu_foreign -- the columns of the above keys
    on fks.TABLE_CATALOG = kcu_foreign.TABLE_CATALOG
    and fks.TABLE_SCHEMA = kcu_foreign.TABLE_SCHEMA
    and fks.TABLE_NAME = kcu_foreign.TABLE_NAME
    and fks.CONSTRAINT_NAME = kcu_foreign.CONSTRAINT_NAME
inner join INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc -- referenced constraints
    on rc.CONSTRAINT_CATALOG = fks.CONSTRAINT_CATALOG
    and rc.CONSTRAINT_SCHEMA = fks.CONSTRAINT_SCHEMA
    and rc.CONSTRAINT_NAME = fks.CONSTRAINT_NAME
inner join INFORMATION_SCHEMA.TABLE_CONSTRAINTS pks -- primary keys (referenced by fks)
    on rc.UNIQUE_CONSTRAINT_CATALOG = pks.CONSTRAINT_CATALOG
    and rc.UNIQUE_CONSTRAINT_SCHEMA = pks.CONSTRAINT_SCHEMA
    and rc.UNIQUE_CONSTRAINT_NAME = pks.CONSTRAINT_NAME
inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu_primary
    on pks.TABLE_CATALOG = kcu_primary.TABLE_CATALOG
    and pks.TABLE_SCHEMA = kcu_primary.TABLE_SCHEMA
    and pks.TABLE_NAME = kcu_primary.TABLE_NAME
    and pks.CONSTRAINT_NAME = kcu_primary.CONSTRAINT_NAME
    and kcu_foreign.ORDINAL_POSITION = kcu_primary.ORDINAL_POSITION -- this joins the columns
where fks.TABLE_SCHEMA = 'dbo' -- replace with schema name
and fks.TABLE_NAME = 'your_table_name' -- replace with table name
and fks.CONSTRAINT_TYPE = 'FOREIGN KEY'
and pks.CONSTRAINT_TYPE = 'PRIMARY KEY'
order by fks.constraint_name, kcu_foreign.ORDINAL_POSITION

Anmerkung: Es gibt einige Unterschiede zwischen den Implementierungen von information_schema für potgresql und sqlserver, die auf den beiden Systemen zu unterschiedlichen Ergebnissen führen - einer zeigt Spaltennamen für die Fremdschlüsseltabelle an, der andere für die Primärschlüsseltabelle. Aus diesem Grund habe ich mich dafür entschieden, stattdessen die Ansicht KEY_COLUMN_USAGE zu verwenden.

2
Jakub

kurz aber süß

select  * from information_schema.key_column_usage where constraint_catalog=current_catalog and table_name='your_table_name' and position_in_unique_constraint notnull;
2
nik

Ich habe eine Lösung geschrieben, die häufig gefällt und verwendet wird. Der Code befindet sich unter http://code.google.com/p/pgutils/ . Siehe die pgutils.foreign_keys-Ansicht.

Leider ist die Ausgabe zu wortreich, um sie hier aufzunehmen. Sie können es jedoch an einer öffentlichen Version der Datenbank wie folgt ausprobieren:

$ psql -h unison-db.org -U PUBLIC -d unison -c 'select * from pgutils.foreign_keys;

Dies funktioniert mindestens mit 8.3. Ich erwarte, es in den nächsten Monaten bei Bedarf zu aktualisieren.

-Reece

2
Reece

Ich habe ein kleines Tool erstellt, um das Datenbankschema abzufragen und dann zu vergleichen: PostgreSQL-DB-Schema ausgeben auf Text .

Es gibt Informationen über FK, aber die Antwort von ollyc gibt mehr Details.

1
Michał Niklas

Ein anderer Weg:

WITH foreign_keys AS (
    SELECT
      conname,
      conrelid,
      confrelid,
      unnest(conkey)  AS conkey,
      unnest(confkey) AS confkey
    FROM pg_constraint
    WHERE contype = 'f' -- AND confrelid::regclass = 'your_table'::regclass
)
-- if confrelid, conname pair shows up more than once then it is multicolumn foreign key
SELECT fk.conname as constraint_name,
       fk.confrelid::regclass as referenced_table, af.attname as pkcol,
       fk.conrelid::regclass as referencing_table, a.attname as fkcol
FROM foreign_keys fk
JOIN pg_attribute af ON af.attnum = fk.confkey AND af.attrelid = fk.confrelid
JOIN pg_attribute a ON a.attnum = conkey AND a.attrelid = fk.conrelid
ORDER BY fk.confrelid, fk.conname
;
1
Konrad Perzyna

Anmerkung: Vergessen Sie nicht die Reihenfolge der Spalten beim Lesen der Randbedingungsspalten!

SELECT conname, attname
  FROM pg_catalog.pg_constraint c 
  JOIN pg_catalog.pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = ANY (c.conkey)
 WHERE attrelid = 'schema.table_name'::regclass
 ORDER BY conname, array_position(c.conkey, a.attnum)
0
Nashev

Das ist, was ich gerade verwende, es wird eine Tabelle und ihre fkey-Einschränkungen [listet die Klausel entfernen und alle Tabellen im aktuellen Katalog auflisten]: 

SELECT

    current_schema() AS "schema",
    current_catalog AS "database",
    "pg_constraint".conrelid::regclass::text AS "primary_table_name",
    "pg_constraint".confrelid::regclass::text AS "foreign_table_name",

    (
        string_to_array(
            (
                string_to_array(
                    pg_get_constraintdef("pg_constraint".oid),
                    '('
                )
            )[2],
            ')'
        )
    )[1] AS "foreign_column_name",

    "pg_constraint".conindid::regclass::text AS "constraint_name",

    TRIM((
        string_to_array(
            pg_get_constraintdef("pg_constraint".oid),
            '('
        )
    )[1]) AS "constraint_type",

    pg_get_constraintdef("pg_constraint".oid) AS "constraint_definition"

FROM pg_constraint AS "pg_constraint"

JOIN pg_namespace AS "pg_namespace" ON "pg_namespace".oid = "pg_constraint".connamespace

WHERE
    --fkey and pkey constraints
    "pg_constraint".contype IN ( 'f', 'p' )
    AND
    "pg_namespace".nspname = current_schema()
    AND
    "pg_constraint".conrelid::regclass::text IN ('whatever_table_name')
0
hajikelist