Jak znaleźć zależności kluczy obcych w SQL Server?

163

Jak mogę znaleźć wszystkie zależności klucza obcego w określonej kolumnie?

Jakie są różne alternatywy (graficznie w SSMS, zapytania / widoki w SQL Server, narzędzia bazodanowe innych firm, kod w .NET)?

Nawet Mien
źródło

Odpowiedzi:

290

Poniższe zapytanie pomoże Ci zacząć. Zawiera listę wszystkich relacji z kluczami zagranicznymi w bieżącej bazie danych.

SELECT
    FK_Table = FK.TABLE_NAME,
    FK_Column = CU.COLUMN_NAME,
    PK_Table = PK.TABLE_NAME,
    PK_Column = PT.COLUMN_NAME,
    Constraint_Name = C.CONSTRAINT_NAME
FROM
    INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK
    ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK
    ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU
    ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME
INNER JOIN (
            SELECT
                i1.TABLE_NAME,
                i2.COLUMN_NAME
            FROM
                INFORMATION_SCHEMA.TABLE_CONSTRAINTS i1
            INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE i2
                ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME
            WHERE
                i1.CONSTRAINT_TYPE = 'PRIMARY KEY'
           ) PT
    ON PT.TABLE_NAME = PK.TABLE_NAME

Relacje można również wyświetlać graficznie w programie SQL Server Management Studio w ramach diagramów baz danych.

John Sansom
źródło
8
Dzięki! Musiałem tylko dodać << WHERE FK.TABLE_NAME = 'MyTable' AND CU.COLUMN_NAME = 'MyColumn' >>, aby uzyskać określoną kolumnę.
Nawet Mien
1
+1! W razie potrzeby, aby uzyskać określoną kolumnę, ale dla wszystkich tabel, wystarczyłoby „WHERE CU.COLUMN_NAME = 'MyColumn'”.
liang
1
Podobnie do Even - użyłem WHERE PK.TABLE_NAME = 'MyTable', aby znaleźć tabelę, od której zależało.
Lanceomagnifico
7
@samkitshah: Nikt nie powiedział, że tak. Pytanie jest oznaczone tagiem sql-server, który z definicji jest technologią firmy Microsoft. Postgres nie ma z tym nic wspólnego.
Neolisk
2
-1: To zapytanie pomija klucze obce, które są wspierane przez unikalne ograniczenie lub unikalny indeks, a nie przez klucz podstawowy, w tabeli, do której się odwołuje. Według MSDN : „Ograniczenie klucza obcego nie musi być połączone tylko z ograniczeniem klucza podstawowego w innej tabeli; można go również zdefiniować w celu odniesienia do kolumn ograniczenia UNIQUE w innej tabeli. ” Odpowiedź można sprawić, aby działała z unikalnymi ograniczeniami, usuwając ostatnie sprzężenie, oraz z unikalnymi indeksami, usuwając ostatnie dwa łączenia, ale to ogranicza zwracane informacje.
Douglas
100

próbować: sp_help [table_name]

otrzymasz wszystkie informacje o tabeli, w tym wszystkie klucze obce

Andrija
źródło
2
niezły, bardzo przydatny. Bardziej zapadająca w pamięć niż zaznaczona odpowiedź! Nie mogę uwierzyć, że nie możesz ich po prostu zobaczyć w SSMS!
JonnyRaa
4
Bardzo fajnie, dziękuję. Ale szukając FK, wolę wynik z odpowiedzi Michaela poniżej: sp_fkeys [table]
AjV Jsy
.... lub jeśli nie uzyskasz żadnych wyników (ale sp_help WYŚWIETLA klucze obce), sp_fkeys @fktable_name='TableName'
pełna
wspaniały! krótkie i zwięzłe!
zeroflaw
39

Jeśli planujesz usunąć lub zmienić nazwę tabeli lub kolumny, znalezienie tylko zależności klucza obcego może nie wystarczyć.

Odwoływanie się do tabel niezwiązanych z kluczem obcym - Będziesz także musiał wyszukać tabele odwołań, które mogą nie być połączone z kluczem obcym (widziałem wiele baz danych o złym projekcie, które nie mają zdefiniowanych kluczy obcych, ale mają powiązane dane ). Rozwiązaniem może być wyszukanie nazwy kolumny we wszystkich tabelach i poszukanie podobnych kolumn.

Inne obiekty bazy danych - to prawdopodobnie trochę nie na temat, ale jeśli szukałeś wszystkich referencji, ważne jest również, aby sprawdzić zależne obiekty.

Narzędzia GUI - Wypróbuj opcję „Znajdź powiązane obiekty” programu SSMS lub narzędzia, takie jak ApexSQL Search (bezpłatne narzędzie, integruje się z SSMS), aby zidentyfikować wszystkie zależne obiekty, w tym tabele powiązane z kluczem obcym.

Timothy Walden
źródło
39

Ponieważ Twoje pytanie dotyczy jednej tabeli, możesz użyć tego:

EXEC sp_fkeys 'TableName'

Znalazłem to na SO tutaj:

https://stackoverflow.com/a/12956348/652519

Dość szybko znalazłem potrzebne informacje. Zawiera tabelę, kolumnę i nazwę klucza obcego.

EDYTOWAĆ

Oto link do dokumentacji, w której wyszczególniono różne parametry, których można użyć: https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-fkeys-transact-sql

Michael
źródło
29

Myślę, że ten skrypt jest tańszy:

SELECT f.name AS ForeignKey, OBJECT_NAME(f.parent_object_id) AS TableName,
    COL_NAME(fc.parent_object_id, fc.parent_column_id) AS ColumnName,
    OBJECT_NAME (f.referenced_object_id) AS ReferenceTableName,
    COL_NAME(fc.referenced_object_id, fc.referenced_column_id) AS ReferenceColumnName
FROM sys.foreign_keys AS f
INNER JOIN sys.foreign_key_columns AS fc
ON f.OBJECT_ID = fc.constraint_object_id
RobSiklos
źródło
6

Ten, którego naprawdę lubię używać, nazywa się SQL Dependency Tracker firmy Red Gate Software . Możesz umieścić dowolne obiekty bazy danych, takie jak tabele, procedury składowane itp., A następnie automatycznie narysuje linie relacji między wszystkimi innymi obiektami, które opierają się na wybranych elementach.

Daje bardzo dobrą graficzną reprezentację zależności w twoim schemacie.

TheTXI
źródło
2
Jest to również świetne narzędzie do pokazania nietechnicznym osobom, że muszą wydać trochę pieniędzy na refaktoryzację projektu bazy danych, zanim wszystko się rozpadnie. Wykresy, które generuje, są dość atrakcyjne.
Rob Allen
1
Rob: Lubię ładować tam cały schemat bazy danych, a następnie przełączać się między różnymi układami, aby móc obserwować, jak wszystkie rzeczy latają.
TheTXI
4

Wielkie dzięki dla Johna Sansoma, jego zapytanie jest wspaniałe!

Ponadto: na końcu zapytania należy dodać „AND PT.ORDINAL_POSITION = CU.ORDINAL_POSITION”.

Jeśli masz wiele pól w kluczu podstawowym, ta instrukcja dopasuje do siebie odpowiednie pola (miałem przypadek, twoje zapytanie utworzyło wszystkie kombinacje, więc dla 2 pól w kluczu podstawowym miałem 4 wyniki dla odpowiedniego klucza obcego) .

(Przepraszam, że nie mogę skomentować odpowiedzi Johna, ponieważ nie mam wystarczającej liczby punktów reputacji).

Sierramike
źródło
3

To zapytanie zwróci szczegóły dotyczące kluczy obcych w tabeli, obsługuje wiele kluczy kolumn.

    SELECT *
    FROM
    (
    SELECT 
    T1.constraint_name ConstraintName,
    T2.COLUMN_NAME ColumnName,
    T3.TABLE_NAME RefTableName, 
    T3.COLUMN_NAME RefColumnName,
    T1.MATCH_OPTION MatchOption, 
    T1.UPDATE_RULE UpdateRule, 
    T1.DELETE_RULE DeleteRule
    FROM 
    INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS T1
    INNER JOIN
    INFORMATION_SCHEMA.KEY_COLUMN_USAGE T2 
    ON T1.CONSTRAINT_NAME = T2.CONSTRAINT_NAME
    INNER JOIN
    INFORMATION_SCHEMA.KEY_COLUMN_USAGE T3 
    ON T1.UNIQUE_CONSTRAINT_NAME = T3.CONSTRAINT_NAME 
    AND T2.ORDINAL_POSITION = T3.ORDINAL_POSITION) A
    WHERE A.ConstraintName = 'table_name'
Kumait
źródło
2

Po długich poszukiwaniach znalazłem działające rozwiązanie. Moja baza danych nie używa sys.foreign_key_columns, a information_schema.key_column_usage zawiera tylko klucze podstawowe.

Używam SQL Server 2015

ROZWIĄZANIE 1 (rzadko używane)

Jeśli inne rozwiązania nie działają, to będzie działać dobrze:

        WITH CTE AS
        (
            SELECT 
                TAB.schema_id,
                TAB.name,
                COL.name AS COLNAME,
                COl.is_identity
            FROM 
                sys.tables TAB INNER JOIN sys.columns COL 
                    ON TAB.object_id = COL.object_id
        )
        SELECT 
            DB_NAME() AS [Database], 
            SCHEMA_NAME(Child.schema_id) AS 'Schema',
            Child.name AS 'ChildTable',
            Child.COLNAME AS 'ChildColumn',
            Parent.name AS 'ParentTable',
            Parent.COLNAME AS 'ParentColumn'
        FROM 
            cte Child INNER JOIN CTE Parent
                ON 
                    Child.COLNAME=Parent.COLNAME AND 
                    Child.name<>Parent.name AND 
                    Child.is_identity+1=Parent.is_identity

ROZWIĄZANIE 2 (powszechnie używane)

W większości przypadków zadziała to dobrze:

        SELECT
            DB_NAME() AS [Database], 
            SCHEMA_NAME(fk.schema_id) AS 'Schema',
            fk.name 'Name',
            tp.name 'ParentTable',
            cp.name 'ParentColumn',
            cp.column_id,
            tr.name 'ChildTable',
            cr.name 'ChildColumn',
            cr.column_id
        FROM
            sys.foreign_keys fk
        INNER JOIN
            sys.tables tp ON fk.parent_object_id = tp.object_id
        INNER JOIN
            sys.tables tr ON fk.referenced_object_id = tr.object_id
        INNER JOIN
            sys.foreign_key_columns fkc ON fkc.constraint_object_id = fk.object_id
        INNER JOIN
            sys.columns cp ON fkc.parent_column_id = cp.column_id AND fkc.parent_object_id = cp.object_id
        INNER JOIN
            sys.columns cr ON fkc.referenced_column_id = cr.column_id AND fkc.referenced_object_id = cr.object_id
        WHERE 
            -- CONCAT(SCHEMA_NAME(fk.schema_id), '.', tp.name, '.', cp.name) LIKE '%my_table_name%' OR
            -- CONCAT(SCHEMA_NAME(fk.schema_id), '.', tr.name, '.', cr.name) LIKE '%my_table_name%' 
        ORDER BY
            tp.name, cp.column_id
profimedica
źródło
2

Możesz użyć INFORMATION_SCHEMA.KEY_COLUMN_USAGE i sys.foreign_key_columns, aby uzyskać metadane klucza obcego dla tabeli, tj. Nazwę ograniczenia, tabelę referencyjną i kolumnę referencyjną itp.

Poniżej znajduje się zapytanie:

SELECT  CONSTRAINT_NAME, COLUMN_NAME, ParentTableName, RefTableName,RefColName FROM 
    (SELECT CONSTRAINT_NAME,COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME = '<tableName>') constraint_details
    INNER JOIN  
    (SELECT ParentTableName, RefTableName,name ,COL_NAME(fc.referenced_object_id,fc.referenced_column_id) RefColName  FROM (SELECT object_name(parent_object_id) ParentTableName,object_name(referenced_object_id) RefTableName,name,OBJECT_ID  FROM sys.foreign_keys WHERE parent_object_id = object_id('<tableName>') ) f 
    INNER JOIN   
    sys.foreign_key_columns AS fc  ON  f.OBJECT_ID = fc.constraint_object_id ) foreign_key_detail 
    on foreign_key_detail.name = constraint_details.CONSTRAINT_NAME
Shreyansh Bothra
źródło
1

Tylko uwaga do odpowiedzi @ „John Sansom”,

Jeśli szukane są zależności klucza obcego , myślę, że klauzula PT Where powinna być:

i1.CONSTRAINT_TYPE = 'FOREIGN KEY'  -- instead of 'PRIMARY KEY'

i jest włączony stan:

ON PT.TABLE_NAME = FK.TABLE_NAME  instead of PK.TABLE_NAME

Ponieważ powszechnie używany jest klucz podstawowy tabeli obcej, myślę, że ten problem nie został wcześniej zauważony.

Homo Programmatoris
źródło
0
SELECT  obj.name AS FK_NAME,
    sch.name AS [schema_name],
    tab1.name AS [table],
    col1.name AS [column],
    tab2.name AS [referenced_table],
    col2.name AS [referenced_column]
FROM sys.foreign_key_columns fkc
INNER JOIN sys.objects obj
    ON obj.object_id = fkc.constraint_object_id
INNER JOIN sys.tables tab1
    ON tab1.object_id = fkc.parent_object_id
INNER JOIN sys.schemas sch
    ON tab1.schema_id = sch.schema_id
INNER JOIN sys.columns col1
    ON col1.column_id = parent_column_id AND col1.object_id = tab1.object_id
INNER JOIN sys.tables tab2
    ON tab2.object_id = fkc.referenced_object_id
INNER JOIN sys.columns col2
    ON col2.column_id = referenced_column_id AND col2.object_id = tab2.object_id

To da ci:

Sam FK Schemat, do którego należy FK

  • „Tabela referencyjna” lub tabela, która ma FK
  • „Kolumna odniesienia” lub kolumna wewnątrz tabeli odwołań, która wskazuje na FK
  • „Tabela, do której istnieją odwołania” lub tabela zawierająca kolumnę kluczową, na którą wskazuje Twój FK
  • „Kolumna, do której istnieje odwołanie” lub kolumna będąca kluczem, na który wskazuje Twój FK
Divya Agrawal
źródło
-1

UŻYJ schematu information_schema;

SELECT COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME
FROM KEY_COLUMN_USAGE
WHERE (table_name = *tablename*) AND NOT (REFERENCED_TABLE_NAME IS NULL)
dave
źródło