Finden Sie die Fremdschlüssel, die einem bestimmten Primärschlüssel zugeordnet sind

19

Ich möchte einen Weg finden, um festzustellen, welche Spalten in einer bestimmten Datenbank über PK / FK-Beziehungen verbunden sind. Ich kann die PK / FK-Informationen für eine bestimmte Tabelle über zurückgeben

SELECT *  
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS cu 
WHERE EXISTS (
    SELECT tc.* 
    FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS tc 
    WHERE tc.CONSTRAINT_CATALOG = 'MyDatabase'  
        AND tc.TABLE_NAME = 'MyTable'  
        /*AND tc.CONSTRAINT_TYPE = 'PRIMARY KEY'*/
        AND tc.CONSTRAINT_NAME = cu.CONSTRAINT_NAME);
GO

aber für eine PK, die von einer solchen Abfrage zurückgegeben wird, wie stelle ich die zugehörige FK her (vorausgesetzt, es gibt eine)?

Ich weiß, dass Sie die referenzierten Tabellen auch erhalten können über:

SELECT CONSTRAINT_NAME = name, 
       FOREIGN_SCHEMA = OBJECT_SCHEMA_NAME(parent_object_id), 
       FOREIGN_TABLE = OBJECT_NAME(parent_object_id), 
       REFERENCED_SCHEMA = OBJECT_SCHEMA_NAME(referenced_object_id), 
       REFERENCED_TABLE = OBJECT_NAME(referenced_object_id) 
FROM sys.foreign_keys
WHERE OBJECT_NAME(referenced_object_id) = 'MyTable';
GO

aber ich kämpfe jetzt, um die expliziten Spaltenreferenzen zu erhalten.

Ich erstelle einen Skriptgenerator für QlikView. Um das Skript zu generieren, benötige ich die Einschränkungen und die zugehörigen Links. Ich benötige alle Informationen zur Einschränkung für eine bestimmte Spalte (falls vorhanden).

Ich möchte eine Datenbankklasse erstellen, die alle Informationen für eine bestimmte Datenbank enthält. Diese Klassenstruktur database.table.column.constraintswird dann verwendet, um die Übereinstimmungen zwischen verschiedenen Spalten in PK / FKs abzurufen.

Es ist klar, dass einige Spalten nur FKs enthalten. In diesem Fall möchte ich auch die PK-Informationen des entsprechenden Schlüssels abrufen. Einige werden nur PKs haben und dann möchte ich das Gegenteil. Einige können natürlich beides haben.

Mond Ritter
quelle

Antworten:

35

Hier ist eine einfache Abfrage, um Fremdschlüssel mit ihren referenzierten Tabellen / Spalten abzugleichen:

SELECT
    o1.name AS FK_table,
    c1.name AS FK_column,
    fk.name AS FK_name,
    o2.name AS PK_table,
    c2.name AS PK_column,
    pk.name AS PK_name,
    fk.delete_referential_action_desc AS Delete_Action,
    fk.update_referential_action_desc AS Update_Action
FROM sys.objects o1
    INNER JOIN sys.foreign_keys fk
        ON o1.object_id = fk.parent_object_id
    INNER JOIN sys.foreign_key_columns fkc
        ON fk.object_id = fkc.constraint_object_id
    INNER JOIN sys.columns c1
        ON fkc.parent_object_id = c1.object_id
        AND fkc.parent_column_id = c1.column_id
    INNER JOIN sys.columns c2
        ON fkc.referenced_object_id = c2.object_id
        AND fkc.referenced_column_id = c2.column_id
    INNER JOIN sys.objects o2
        ON fk.referenced_object_id = o2.object_id
    INNER JOIN sys.key_constraints pk
        ON fk.referenced_object_id = pk.parent_object_id
        AND fk.key_index_id = pk.unique_index_id
ORDER BY o1.name, o2.name, fkc.constraint_column_id

Die Ausgabe enthält acht Spalten: die Tabellen- und Spaltennamen für die Fremdschlüssel (FK_table, FK_column), die Namen der Fremdschlüsseleinschränkungen (FK_name), die referenzierten PK- oder eindeutigen Indextabellen- und Spaltennamen (PK_table, PK_column), die Name der referenzierten PK oder des eindeutigen Index (PK_name) und der Kaskadenaktionen zum Aktualisieren / Löschen (Delete_Action, Update_Action).

(Bearbeitet, um weitere Ausgabespalten hinzuzufügen.)

EDIT: Ich bin 6 Jahre später mit einer verbesserten Version zurück. Ich stellte fest, dass die ursprüngliche Abfrage mehrspaltige Fremdschlüssel nicht wirklich gut verarbeitet, und ich wollte auch in der Lage sein, deaktivierte, nicht vertrauenswürdige oder nicht indizierte Fremdschlüssel schnell zu identifizieren. Hier ist die neue Version, die all das korrigiert.

Mehrspaltige Schlüssel werden als durch Kommas getrennte Listen in FK_columnsund PK_columnsunter Verwendung des traditionellen FOR XML/ STUFFMissbrauchs angezeigt . In der FK_indexesSpalte werden die Namen aller Indizes in der Fremdschlüsseltabelle angezeigt, die möglicherweise zur Erfüllung von Suchanfragen mithilfe der Fremdschlüsselspalten verwendet werden könnten (hauptsächlich zur Optimierung von Lösch- oder Aktualisierungsvorgängen in der Primärschlüsseltabelle). Wenn NULLja, haben Sie einen nicht indizierten Fremdschlüssel. Sie können das optimieren ORDER BYoder eine WHEREKlausel hinzufügen (unten auskommentiert ), wenn Sie nach dem PK-Tabellennamen sortieren, nach bestimmten PK / FK-Tabellen filtern möchten usw.

SELECT
    fk.is_disabled,
    fk.is_not_trusted,
    OBJECT_SCHEMA_NAME(o1.object_id) AS FK_schema,
    o1.name AS FK_table,
    --Generate list of columns in referring side of foreign key
    STUFF(
        (
            SELECT ', ' + c1.name AS [text()]
            FROM sys.columns c1 INNER
                JOIN sys.foreign_key_columns fkc
                    ON c1.object_id = fkc.parent_object_id
                    AND c1.column_id = fkc.parent_column_id
            WHERE fkc.constraint_object_id = fk.object_id
            FOR XML PATH('')
        ), 1, 2, '') AS FK_columns,
    --Look for any indexes that will fully satisfy the foreign key columns
    STUFF(
        (
            SELECT ', ' + i.name AS [text()]
            FROM sys.indexes i
            WHERE i.object_id = o1.object_id
                AND NOT EXISTS ( --Find foreign key columns that don't match the index key columns
                    SELECT fkc.constraint_column_id, fkc.parent_column_id
                    FROM sys.foreign_key_columns fkc
                    WHERE fkc.constraint_object_id = fk.object_id
                    EXCEPT
                    SELECT ic.key_ordinal, ic.column_id
                    FROM sys.index_columns ic
                    WHERE ic.object_id = i.object_id AND ic.index_id = i.index_id
                )
            FOR XML PATH('')
        ), 1, 2, '') AS FK_indexes,
    fk.name AS FK_name,
    OBJECT_SCHEMA_NAME(o2.object_id) AS PK_schema,
    o2.name AS PK_table,
    --Generate list of columns in referenced (i.e. PK) side of foreign key
    STUFF(
        (
            SELECT ', ' + c2.name AS [text()]
            FROM sys.columns c2
                INNER JOIN sys.foreign_key_columns fkc
                    ON c2.object_id = fkc.referenced_object_id
                    AND c2.column_id = fkc.referenced_column_id
            WHERE fkc.constraint_object_id = fk.object_id
            FOR XML PATH('')
        ), 1, 2, '') AS PK_columns,
    pk.name AS PK_name,
    fk.delete_referential_action_desc AS Delete_Action,
    fk.update_referential_action_desc AS Update_Action
FROM sys.objects o1
    INNER JOIN sys.foreign_keys fk
        ON o1.object_id = fk.parent_object_id
    INNER JOIN sys.objects o2
        ON fk.referenced_object_id = o2.object_id
    INNER JOIN sys.key_constraints pk
        ON fk.referenced_object_id = pk.parent_object_id
        AND fk.key_index_id = pk.unique_index_id
--WHERE o2.name = 'Company_Address'
ORDER BY o1.name, o2.name
db2
quelle
7

Diese Abfrage fasst alle FK-Beziehungen in der Datenbank zusammen - den Namen der FK-Einschränkung, das Schema / die Tabelle der referenzierenden Tabelle, den Namen der referenzierenden Spalte, das Schema / die Tabelle der referenzierten Tabelle und den Namen der referenzierten Spalte. Bei einer mehrspaltigen Einschränkung gibt es mehrere Zeilen.

SELECT 
    FK = OBJECT_NAME(pt.constraint_object_id),
    Referencing_table = QUOTENAME(OBJECT_SCHEMA_NAME(pt.parent_object_id))
            + '.' + QUOTENAME(OBJECT_NAME(pt.parent_object_id)),
    Referencing_col = QUOTENAME(pc.name), 
    Referenced_table = QUOTENAME(OBJECT_SCHEMA_NAME(pt.referenced_object_id)) 
            + '.' + QUOTENAME(OBJECT_NAME(pt.referenced_object_id)),
    Referenced_col = QUOTENAME(rc.name)
FROM sys.foreign_key_columns AS pt
INNER JOIN sys.columns AS pc
ON pt.parent_object_id = pc.[object_id]
AND pt.parent_column_id = pc.column_id
INNER JOIN sys.columns AS rc
ON pt.referenced_column_id = rc.column_id
AND pt.referenced_object_id = rc.[object_id]
ORDER BY Referencing_table, FK, pt.constraint_column_id;

Wenn Sie nach den Spalten einer bestimmten Primärschlüsseleinschränkung suchen und den Namen dieser PK-Einschränkung bereits kennen, können Sie Folgendes schreiben:

DECLARE @PK_Constraint SYSNAME = N'Name of PK constraint';

SELECT
    FK = OBJECT_NAME(fkc.constraint_object_id),
    Referencing_table = QUOTENAME(OBJECT_SCHEMA_NAME(fkc.parent_object_id))
            + '.' + QUOTENAME(OBJECT_NAME(fkc.parent_object_id)),
    Referencing_col = QUOTENAME(pc.name), 
    Referenced_table = QUOTENAME(OBJECT_SCHEMA_NAME(fkc.referenced_object_id)) 
            + '.' + QUOTENAME(OBJECT_NAME(fkc.referenced_object_id)),
    Referenced_col = QUOTENAME(rc.name)
FROM sys.foreign_key_columns AS fkc
INNER JOIN sys.columns AS pc
ON fkc.parent_object_id = pc.[object_id]
AND fkc.parent_column_id = pc.column_id
INNER JOIN sys.columns AS rc
ON fkc.referenced_column_id = rc.column_id
AND fkc.referenced_object_id = rc.[object_id]
WHERE EXISTS 
(
  SELECT 1 FROM sys.indexes AS i
  INNER JOIN sys.foreign_keys AS fk
  ON i.[object_id] = fk.referenced_object_id
  AND i.index_id = fk.key_index_id
  AND fk.[object_id] = fkc.constraint_object_id
  AND i.name = @PK_Constraint
)
ORDER BY Referencing_table, FK, fkc.constraint_column_id;

Wenn Sie nur den PK-Namen zusammen mit den anderen Informationen einfügen möchten:

SELECT 
    FK = OBJECT_NAME(fkc.constraint_object_id),
    Referencing_table = QUOTENAME(OBJECT_SCHEMA_NAME(fkc.parent_object_id))
            + '.' + QUOTENAME(OBJECT_NAME(fkc.parent_object_id)),
    Referencing_col = QUOTENAME(pc.name),
    Referenced_table = QUOTENAME(OBJECT_SCHEMA_NAME(fkc.referenced_object_id)) 
            + '.' + QUOTENAME(OBJECT_NAME(fkc.referenced_object_id)),
    Referenced_col = QUOTENAME(rc.name),
    PK = pk.name
FROM sys.foreign_key_columns AS fkc
INNER JOIN sys.columns AS pc
ON fkc.parent_object_id = pc.[object_id]
AND fkc.parent_column_id = pc.column_id
INNER JOIN sys.columns AS rc
ON fkc.referenced_column_id = rc.column_id
AND fkc.referenced_object_id = rc.[object_id]
INNER JOIN (SELECT i.name, fk.[object_id]
  FROM sys.indexes AS i
  INNER JOIN sys.foreign_keys AS fk
  ON i.[object_id] = fk.referenced_object_id
  AND i.index_id = fk.key_index_id
) AS pk
ON pk.[object_id] = fkc.constraint_object_id
ORDER BY Referencing_table, FK, fkc.constraint_column_id;

Es gibt auch Tricks, die Spaltenliste beispielsweise in einer durch Kommas getrennten Liste oder in einzelnen Spalten abzurufen, anstatt sie über Zeilen zu verteilen. Ich werde jedoch erst dann in die Modifikation dieser Abfragen investieren, wenn ich genau weiß, in welcher Form sie vorliegen Du bist hinterher.

Aaron Bertrand
quelle