744 / 5 000
Most of the queries provided in these answers probably overlook the fact that a referential integrity constraint (FK) can consist of multiple columns!
Some use uppercase names, while the SQL identifiers (table names, column names, etc.) of system objects are lowercase, which can be problematic in an instance with CASE-SENSITIVE collation.
Others cast object names by limiting them to 30 characters, whereas the standard SQL and SQL Server uses 128 characters.
Some other omits the management clause ON UPDATE/DELETE...
The query below lists all FK constraints for all tables in the current database, along with the list of foreign key columns and the list of reference keys (PK or UNIQUE).
SELECT sfk.name AS FK_TABLE_SCHEMA, fkt.name AS FK_TABLE_NAME, fk.name AS FK_CONSTRAINT_NAME, substring(CFK.column_names, 1, len(CFK.column_names)-1) AS FK_TARGET_COLUMN_LIST, spk.name AS REF_TABLE_SCHEMA, pkt.name AS REF_TABLE_NAME, substring(CPK.column_names, 1, len(CPK.column_names)-1) AS PK_SOURCE_COLUMN_LIST, delete_referential_action_desc AS DELETE_ACTION, update_referential_action_desc AS UPDATE_ACTION FROM sys.foreign_keys fk INNER JOIN sys.tables fkt ON fkt.object_id = fk.parent_object_id INNER JOIN sys.schemas AS sfk ON fkt.schema_id = sfk.schema_id INNER JOIN sys.tables pkt ON pkt.object_id = fk.referenced_object_id INNER JOIN sys.schemas AS spk ON pkt.schema_id = spk.schema_id CROSS APPLY (SELECT QUOTENAME(col.name) + ', ' FROM sys.foreign_key_columns AS fkcol INNER JOIN sys.columns AS col ON fkcol.parent_object_id = col.object_id and fkcol.parent_column_id = col.column_id where fkcol.parent_object_id = fkt.object_id and fkcol.constraint_object_id = fk.object_id order by fkcol.constraint_column_id for xml path ('') ) AS CFK (column_names) CROSS APPLY (select QUOTENAME(col.name) + ', ' from sys.foreign_key_columns pkcol inner join sys.columns col on pkcol.referenced_object_id = col.object_id and pkcol.referenced_column_id = col.column_id where pkcol.referenced_object_id = pkt.object_id and pkcol.constraint_object_id = fk.object_id order by pkcol.constraint_column_id for xml path ('') ) AS CPK (column_names);
You can test this query with the following tables :
CREATE TABLE A ( CA int NOT NULL, CB int NOT NULL, CC int NOT NULL, PRIMARY KEY (CB, CA, CC) ); CREATE TABLE B ( XX int NOT NULL, YY int NOT NULL, ZZ int NOT NULL, FOREIGN KEY(ZZ, XX, YY) REFERENCES A (CB, CA, CC) );
To have the DROP CONSTRAINT and, if you want, the ADD CONSTRAINT for this list, you can use the CTE :
WITH T0 AS ( ) SELECT N'ALTER TABLE ' + QUOTENAME(FK_TABLE_SCHEMA) + N'.' + QUOTENAME(FK_TABLE_NAME) + N' DROP CONSTRAINT ' + QUOTENAME(FK_CONSTRAINT_NAME) + ';' AS DROP_FK, N'ALTER TABLE ' + QUOTENAME(FK_TABLE_SCHEMA) + N'.' + QUOTENAME(FK_TABLE_NAME) + N' ADD CONSTRAINT ' + QUOTENAME(FK_CONSTRAINT_NAME) + N'(' + FK_TARGET_COLUMN_LIST + N')' + N' REFERENCES ' + QUOTENAME(REF_TABLE_SCHEMA) + N'.' + QUOTENAME(REF_TABLE_NAME) + N')' + N' ON UPDATE ' + UPDATE_ACTION COLLATE database_default + N' ON DELETE ' + DELETE_ACTION COLLATE database_default + ';' AS ADD_FK FROM T0;
And add the firts query between the parenthesis of the CTE...
Another solution is to use the standard ISO SQL views of the INFORMATION_SCHEMA SQL schema and standardized functionnality (no CROSS APPLY as an example) :
WITH T0 AS ( SELECT fkt.TABLE_SCHEMA AS FK_TABLE_SCHEMA, fkt.TABLE_NAME AS FK_TABLE_NAME, fk.CONSTRAINT_NAME AS FK_CONSTRAINT_NAME, pkt.TABLE_SCHEMA AS REF_TABLE_SCHEMA, pkt.TABLE_NAME AS REF_TABLE_NAME, pkt.CONSTRAINT_NAME AS REF_CONSTRAINT_NAME, fk.DELETE_RULE AS DELETE_ACTION, fk.UPDATE_RULE AS UPDATE_ACTION, (SELECT COUNT(*) FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU WHERE KCU.CONSTRAINT_SCHEMA = fkt.TABLE_SCHEMA AND KCU.CONSTRAINT_NAME = fk.CONSTRAINT_NAME) AS N FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS fk INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS fkt ON fkt.CONSTRAINT_SCHEMA = fk.CONSTRAINT_SCHEMA AND fkt.CONSTRAINT_NAME = fk.CONSTRAINT_NAME INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS pkt ON pkt.CONSTRAINT_SCHEMA = fk.UNIQUE_CONSTRAINT_SCHEMA AND pkt.CONSTRAINT_NAME = fk.UNIQUE_CONSTRAINT_NAME ), -- recursive query to list columns of FK FKC AS ( SELECT KCU.CONSTRAINT_SCHEMA , KCU.CONSTRAINT_NAME, KCU.ORDINAL_POSITION, CAST(KCU.COLUMN_NAME AS NVARCHAR(max)) AS FK_COLS FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU JOIN T0 ON KCU.CONSTRAINT_SCHEMA = T0.FK_TABLE_SCHEMA AND KCU.CONSTRAINT_NAME = T0.FK_CONSTRAINT_NAME WHERE ORDINAL_POSITION = 1 UNION ALL SELECT KCU.CONSTRAINT_SCHEMA , KCU.CONSTRAINT_NAME, KCU.ORDINAL_POSITION, FKC.FK_COLS + ', ' + KCU.COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU JOIN T0 ON KCU.CONSTRAINT_SCHEMA = T0.FK_TABLE_SCHEMA AND KCU.CONSTRAINT_NAME = T0.FK_CONSTRAINT_NAME JOIN FKC ON FKC.CONSTRAINT_SCHEMA = KCU.CONSTRAINT_SCHEMA AND FKC.CONSTRAINT_NAME = KCU.CONSTRAINT_NAME AND FKC.ORDINAL_POSITION + 1 = KCU.ORDINAL_POSITION ), -- recursive query to list columns of PK or UK PKC AS ( SELECT KCU.CONSTRAINT_SCHEMA , KCU.CONSTRAINT_NAME, KCU.ORDINAL_POSITION, CAST(KCU.COLUMN_NAME AS NVARCHAR(max)) AS PK_COLS FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU JOIN T0 ON KCU.CONSTRAINT_SCHEMA = T0.REF_TABLE_SCHEMA AND KCU.CONSTRAINT_NAME = T0.REF_CONSTRAINT_NAME WHERE ORDINAL_POSITION = 1 UNION ALL SELECT KCU.CONSTRAINT_SCHEMA , KCU.CONSTRAINT_NAME, KCU.ORDINAL_POSITION, PKC.PK_COLS + ', ' + KCU.COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU JOIN T0 ON KCU.CONSTRAINT_SCHEMA = T0.REF_TABLE_SCHEMA AND KCU.CONSTRAINT_NAME = T0.REF_CONSTRAINT_NAME JOIN PKC ON PKC.CONSTRAINT_SCHEMA = KCU.CONSTRAINT_SCHEMA AND PKC.CONSTRAINT_NAME = KCU.CONSTRAINT_NAME AND PKC.ORDINAL_POSITION + 1 = KCU.ORDINAL_POSITION ) SELECT FK_TABLE_SCHEMA, FK_TABLE_NAME, FK_CONSTRAINT_NAME, FK_COLS, REF_TABLE_SCHEMA, REF_TABLE_NAME, PK_COLS, UPDATE_ACTION, DELETE_ACTION FROM T0 JOIN FKC ON T0.FK_TABLE_SCHEMA = FKC.CONSTRAINT_SCHEMA AND T0.FK_CONSTRAINT_NAME = FKC.CONSTRAINT_NAME JOIN PKC ON T0.REF_TABLE_SCHEMA = PKC.CONSTRAINT_SCHEMA AND T0.REF_CONSTRAINT_NAME = PKC.CONSTRAINT_NAME WHERE N = FKC.ORDINAL_POSITION AND N = PKC.ORDINAL_POSITION;