Temukan Kunci Asing Terkait dengan Kunci Utama Diberikan

19

Saya ingin cara untuk menetapkan kolom mana dalam basis data yang diberikan bergabung melalui hubungan PK / FK. Saya dapat mengembalikan informasi PK / FK untuk tabel yang diberikan melalui

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

tetapi untuk PK yang dikembalikan dari permintaan seperti itu, bagaimana cara menetapkan FK terkait (dengan asumsi ada satu)?

Saya tahu Anda juga bisa mendapatkan tabel yang direferensikan melalui:

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

tapi sekarang saya berjuang untuk mendapatkan referensi kolom eksplisit.

Saya membuat generator skrip untuk QlikView. Untuk menghasilkan skrip, saya memerlukan batasan dan tautan terkait. Saya membutuhkan semua informasi kendala untuk setiap kolom yang diberikan (jika ada).

Saya ingin membangun kelas basis data yang menampung semua informasi untuk basis data yang diberikan. Struktur kelas database.table.column.constraintsini kemudian akan digunakan untuk mendapatkan kecocokan antara kolom yang berbeda pada PK / FK.

Jelas beberapa kolom hanya memiliki FK dan dalam hal ini saya juga ingin mengambil informasi PK dari kunci yang sesuai; beberapa hanya akan memiliki PK dan kemudian saya ingin sebaliknya. Beberapa tentu saja dapat memiliki keduanya.

Ksatria bulan
sumber

Jawaban:

35

Berikut ini pertanyaan sederhana untuk mencocokkan kunci asing dengan tabel / kolom yang dirujuk:

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

Output memiliki delapan kolom: tabel dan nama kolom untuk kunci asing (FK_table, FK_column), nama batasan kunci asing (FK_name), PK yang dirujuk atau tabel indeks unik dan nama kolom (PK_table, PK_column), yang nama PK yang dirujuk atau indeks unik (PK_name), dan tindakan pembaruan / hapus kaskade (Delete_Action, Update_Action).

(Diedit untuk menambahkan beberapa kolom output lagi.)

EDIT: Saya kembali 6 tahun kemudian dengan versi perbaikan ini. Saya menyadari bahwa kueri asli tidak benar-benar menangani kunci asing multi-kolom dengan baik, dan saya juga ingin dapat dengan cepat mengidentifikasi kunci asing yang dinonaktifkan, tidak dipercaya, atau tidak indeks. Jadi, inilah versi baru yang memperbaiki semua itu.

Kunci multi-kolom ditampilkan sebagai daftar yang dipisahkan koma di FK_columnsdan PK_columns, menggunakan tradisional FOR XML/ STUFFpenyalahgunaan. The FK_indexeskolom menunjukkan nama-nama dari setiap indeks di atas meja asing-kunci yang berpotensi dapat digunakan untuk memuaskan berusaha menggunakan kolom kunci asing (terutama untuk mengoptimalkan menghapus atau update ke meja primary key). Jika ya NULL, maka Anda memiliki kunci asing yang tidak tereksplorasi. Anda dapat mengubah ORDER BY, atau menambahkan WHEREklausa (dikomentari di bawah) jika Anda ingin mengurutkan berdasarkan nama tabel PK, memfilter untuk tabel PK / FK tertentu, dll.

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
sumber
7

Kueri ini menjaring Anda semua hubungan FK dalam database - Nama kendala FK, skema / tabel tabel referensi, referensi nama kolom, skema / tabel tabel referensi, dan nama kolom referensi. Akan ada beberapa baris untuk batasan multi-kolom.

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;

Jika Anda mencari kolom dari batasan kunci primer tertentu , dan Anda sudah tahu nama batasan PK itu, Anda bisa menulis ini:

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;

Jika Anda hanya ingin memasukkan nama PK beserta informasi lainnya:

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;

Ada juga trik untuk mendapatkan daftar kolom di, katakanlah, daftar yang dipisahkan koma atau kolom individual, alih-alih tersebar di seluruh baris, tapi saya tidak akan berinvestasi dalam memodifikasi kueri ini untuk menghasilkan itu sampai saya tahu persis bentuk mana Anda mengejar.

Aaron Bertrand
sumber