Getting Foreign Key Ids from Primary Key - sql

I have an issue, I am not sure if I am going too far with my thinking or not, but the issue is I wanted to query sql server to give me foreign key Ids, column name and table for a specified primary key.
The below query gives me the primary key/foreign key constraint, tables, etc.
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;
A simple table structure would be
create table test.ForeignTable1
(
foreign_key_table_primary_key1 int primary key identity(1,1),
randomdata1 varchar(200) not null
)
create table test.ForeignTable2
(
foreign_key_table_primary_key2 int primary key identity(1,1),
randomdata2 varchar(200) not null
)
create table test.PrimTable
(
primary_key_column int primary key identity(1,1),
ForeignTable1_data int foreign key references test.ForeignTable1(foreign_key_table_primary_key1) ,
ForeignTable2_data int foreign key references test.ForeignTable2(foreign_key_table_primary_key2)
)
The desired output of the sql statement would be to return be, for a particular primary key in PrimTable, it would return all the associated ids in test.ForeignTable2 and test.ForeignTable1 and show which table they come from.
If I supply 1, then it would return Ids (3,7,12) for test.ForeignTable1 and (6,4,458,88) for test.ForeignTable2
Is it possible to query something like that? Also I was wondering how does sql server determine during a delete that you have a foreign key constrain in another table that is preventing you from deleting an item?

SELECT
K_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
WHERE PK.TABLE_NAME=#PK_TABLE_NAME
---- optional:
ORDER BY
1,2,3,4

Just run the following:
EXEC sp_fkeys #pktable_name = '<your_PK_table_name>'

Related

Get Primary Key Value After Insert not knowing the Column Name of the Primary key

I'm trying to figure out if there is a way to return the primary key value, during an insert, without knowing the Primary Keys Column Name.
I would like to use Output because not all of the Primary Keys in the Database are Integers, but I do not think it is possible to do a subquery for an Output.
CREATE TABLE TestTable
(
PkId INT Identity(1,1) NOT NULL,
Value1 VARCHAR(50) NULL,
Value2 VARCHAR(50) NULL,
CONSTRAINT PkId PRIMARY KEY (PkId)
)
INSERT INTO TestTable(Value1, Value2)
OUTPUT INSERTED. --Primary Key to find here
Values('Test', 'Test2')
I was thinking about using something like this as a subquery, but like I stated before I don't think it is possible to use an output with a subquery
SELECT column_name as PRIMARYKEYCOLUMN
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
INNER JOIN
INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KU
ON TC.CONSTRAINT_TYPE = 'PRIMARY KEY' AND
TC.CONSTRAINT_NAME = KU.CONSTRAINT_NAME AND
KU.table_name='TestTable'
ORDER BY KU.TABLE_NAME, KU.ORDINAL_POSITION;
Any help would be greatly appreciated!
You could use your query to find the primary key column name but you'd have to execute dynamic SQL to include that column name in the output clause. I've given you an example here but I've also modified it to use standard system views instead of the old information_schema views.
USE tempdb;
GO
CREATE TABLE dbo.TestTable
(
PkId INT IDENTITY(1,1) NOT NULL
CONSTRAINT PK_dbo_TestTable PRIMARY KEY,
Value1 varchar(50) NULL,
Value2 varchar(50) NULL
);
GO
DECLARE #SQL nvarchar(max) = N'
INSERT INTO TestTable(Value1, Value2)
OUTPUT INSERTED.'
+ QUOTENAME((
SELECT c.[name]
FROM sys.indexes AS i
INNER JOIN sys.index_columns AS ic
ON i.object_id = ic.object_id
AND i.index_id = ic.index_id
INNER JOIN sys.columns AS c
ON i.object_id = c.object_id
AND ic.column_id = c.column_id
INNER JOIN sys.objects AS o
ON i.object_id = o.object_id
INNER JOIN sys.schemas AS s
ON o.schema_id = s.schema_id
WHERE i.is_primary_key <> 0
AND o.[name] = N'TestTable'
AND s.[name] = N'dbo'
)) + N' VALUES(''Test'', ''Test2''), (''Test3'', ''Test4'')';
EXEC (#SQL);
GO
You can run it in tempdb to see how it works. You'll see it outputs the primary key value each time. Where you will have a problem is if there are multiple columns in your primary key. It can also be done but it's much more complicated code. I'm guessing you'll only have one column anyway so this should be fine.
Hope that helps.

Find table relations from SQL Server

I would like to know how to discover which tables are related each other in my SQL Server database by running a SQL query.
As example, suppose I have 2 many-to-many relations and 3 tables initialized with some random values.
create table A (pk int not null primary key identity, A int);
create table B (pk int not null primary key identity, B int);
create table C (pk int not null primary key identity, C int);
create table AB
(
a_pk int not null references A,
b_pk int not null references B,
primary key(a_pk, b_pk)
);
create table AC
(
a_pk int not null references A,
c_pk int not null references C,
primary key(a_pk, c_pk)
);
I would like to run a parametrized query having as output something like:
With parameter 'A':
MM_TAB | REL_TAB
------------------
AB | B
AC | C
With parameter 'B':
MM_TAB | REL_TAB
------------------
AB | A
With parameter 'C':
MM_TAB | REL_TAB
------------------
AC | A
How can I do that?
Check this out:
I created the tables and relations using SQL Server 2014:
create database relation
use relation
create table A (pk int not null primary key identity, A int);
create table B (pk int not null primary key identity, B int);
create table C (pk int not null primary key identity, C int);
create table AB
(
a_pk int not null foreign key references A(pk),
b_pk int not null foreign key references B(pk),
primary key(a_pk, b_pk)
);
create table AC
(
a_pk int not null foreign key references A(pk),
c_pk int not null foreign key references C(pk),
primary key(a_pk, c_pk)
);
Then you can use this query to find the related tables:
use relation
go
DECLARE #table_param NVARCHAR(2)='A';
with rel as(
SELECT
K_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
wHERE PK.TABLE_NAME = #table_param
)
SELECT
K_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
WHERE FK.TABLE_NAME IN(SELECT R.K_Table FROM REL R)

Manually delete cascade on SQL Server

I have a set of tables (SQL Server 2014) and I need to perform a delete cascade but manually. I have these three tables:
CREATE TABLE dbo.A
(
IdKey int identity primary Key,
Name nvarchar(50)
)
CREATE TABLE dbo.B
(
Id int identity primary Key,
ChildIdKey int,
Name nvarchar(50)
)
CREATE TABLE dbo.C
(
Id int identity primary Key,
OtherIdKey int,
Name nvarchar(50)
)
If I need to remove IdKey from table A, I need first to identify the foreign keys pointed to IdKey on table A. I have a FK over ChildIdKey on table B and another on FK over OtherIdKey on table C. Column names are different so now I need to get all tables where I have a FK pointed to IdKey and perform a delete cascade manually and in order to avoid FK errors.
How can I get the list of tables in the correct order for performing the cascade delete manually?
Try this to find out all table depending on a particular column of some-table
DECLARE #ColumnName sysname,#TableName sysname
SET #ColumnName='userId'
SET #TableName='user'
SELECT Fkn.name FKName ,
cname.name FKColumnName,
FCT.name FKContainingTable ,
CCT.name ColumnContainingTable
FROM sys.foreign_key_columns Fk
INNER JOIN sys.foreign_keys fkn ON fkn.object_id=fk.constraint_object_id
INNER JOIN sys.tables FCT ON fCt.object_id = fk.parent_object_id
INNER JOIN sys.tables CCT ON fk.referenced_object_id = CCT.object_id
INNER JOIN sys.columns CLM ON CCT.object_id = clm.object_id
INNER JOIN sys.columns CName ON CName.object_id=fk.parent_object_id AND CName.column_id=fk.parent_column_id
AND CLM.name = #ColumnName
AND cct.name = #TableName

SQL : Check if primary key is foreign key

I want to extract the name of primary key column of table if it is also foreign key from information schema
like two tables :
Student
std_id std_name
And
PHDstudent
std_id reaserchfield
That std_id in PHDstudent is primary key and foreign key at same time that refers to Student table.
Run this query
SELECT Tab.TABLE_NAME, Col.Column_Name from
INFORMATION_SCHEMA.TABLE_CONSTRAINTS Tab,
INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE Col
WHERE
Col.Constraint_Name = Tab.Constraint_Name
AND Col.Table_Name = Tab.Table_Name
AND Constraint_Type = 'PRIMARY KEY '
INTERSECT
SELECT Tab.TABLE_NAME, Col.Column_Name from
INFORMATION_SCHEMA.TABLE_CONSTRAINTS Tab,
INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE Col
WHERE
Col.Constraint_Name = Tab.Constraint_Name
AND Col.Table_Name = Tab.Table_Name
AND Constraint_Type = 'FOREIGN KEY '
SELECT s.std_id FROM Student s
INNER JOIN PHDstudent p
ON s.std_id = p.std_id
Have a read on INNER JOINS
I think you are asking about primary key column name, than you should go with below query which gives you the primary key column name.....
SELECT column_name
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1
AND table_name = 'PHDstudent'

If Foreign Key Not Exist Then Add Foreign Key Constraint(Or Drop a Foreign Key Constraint If Exist) without using Name?

I am finding difficulties to creating a a query. Let say I have a Products and Brands table. I can add a foreign key using this command,
ALTER TABLE Products
ADD FOREIGN KEY (BrandID)
REFERENCES Brands(ID)
But I need to only run this command if Foreign Key does not exist.
A similar thing I need is that drop a Foreign Key Constraint If Exist without using name.
Try this:
IF NOT EXISTS (SELECT * FROM sys.objects o WHERE o.object_id = object_id(N'[dbo].[FK_Products_Brands]') AND OBJECTPROPERTY(o.object_id, N'IsForeignKey') = 1)
BEGIN
ALTER TABLE [dbo].[Products] WITH CHECK ADD CONSTRAINT [FK_Products_Brands] FOREIGN KEY([BrandID]) REFERENCES [dbo].[Brands] ([Id])
END
First of all, you should always name your FKs and all other constraints in order to save yourself trouble like this.
But, if you don't know the name of FK you can check it using multiple system views:
IF NOT EXISTS
(
SELECT * FROM sys.foreign_key_columns fk
INNER JOIN sys.columns pc ON pc.object_id = fk.parent_object_id AND pc.column_id = fk.parent_column_id
INNER JOIN sys.columns rc ON rc.object_id = fk.referenced_object_id AND rc.column_id = fk.referenced_column_id
WHERE fk.parent_object_id = object_id('Products') AND pc.name = 'BrandID'
AND fk.referenced_object_id = object_id('Brands') AND rc.NAME = 'ID'
)
ALTER TABLE Products
ADD CONSTRAINT Your_New_FK_NAME FOREIGN KEY (BrandID)
REFERENCES Brands(ID)
You can use this as well.
IF(OBJECT_ID('FK_Products_Brands', 'F') IS NULL)
ALTER TABLE [dbo].[Products] WITH CHECK ADD CONSTRAINT [FK_Products_Brands] FOREIGN KEY([BrandID]) REFERENCES [dbo].[Brands] ([Id])
To do this without knowing the constraint's name and without inner joins, you can do:
IF NOT EXISTS(SELECT NULL FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE WHERE [TABLE_NAME] = 'Products' AND [COLUMN_NAME] = 'BrandID')
BEGIN
ALTER TABLE Products
ADD FOREIGN KEY (BrandID)
REFERENCES Brands(ID)
END
If you wanted to, you could get the name of the contraint from this table, then do a drop/add.
This worked smoothly for me in Azure sql server.
IF NOT EXISTS (
SELECT NULL
FROM information_schema.TABLE_CONSTRAINTS
WHERE
CONSTRAINT_SCHEMA = 'dbo' AND
CONSTRAINT_NAME = 'FK_company_id' AND
CONSTRAINT_TYPE = 'FOREIGN KEY'
) ALTER TABLE dbo.table_main
ADD CONSTRAINT FK_company_id
FOREIGN KEY (company_id)
REFERENCES dbo.table_company(id);