Find table relations from SQL Server - sql

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)

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.

How to set a primary key?

SELECT TOP 1000 [LicensePlate]
,[Manufacturer]
,[Model]
,[Colour]
,[Year]
,[EngineSize]
,[Value]
FROM [Cars2].[dbo].[Cartable1]
Above is my layout for a cars table. I am completely new to SQL and was wondering how I would set 'Licence Plate' as the primary key?
First find any duplicates by that column.
SELECT
C.LicensePlate,
AmountDuplicates = COUNT(*)
FROM
Cars2.dbo.Cartable1 AS C
GROUP BY
C.LicensePlate
HAVING
COUNT(*) > 1
If any record shows up, you need to either delete all the duplicates or update their license plates so they don't repeat anymore.
You will also need to check for NULL values and update or delete them (primary key can't be null).
SELECT
C.*
FROM
Cars2.dbo.Cartable1 AS C
WHERE
C.LicensePlate IS NULL
Then you can add the PRIMARY KEY constraint with:
ALTER TABLE Cars2.dbo.Cartable1
ADD CONSTRAINT PK_Cartable1 -- Name of the constraint
PRIMARY KEY (LicensePlate)
You might get an error if LicensePlate can hold NULL values. You can change it with an ALTER TABLE:
ALTER TABLE Cars2.dbo.Cartable1 ALTER COLUMN LicensePlate VARCHAR(20) NOT NULL -- The proper data type
If you already have a primary key defined on that table you will have to drop it and then create your new one (one table can only have 1 primary key constraint at a time). You can check which one is it with the following query:
USE Cars2; -- The database name here
DECLARE #TableName VARCHAR(100) = 'Cartable1'
DECLARE #SchemaName VARCHAR(100) = 'dbo'
SELECT
ColumnName = Col.Column_Name,
ConstraintName = tab.CONSTRAINT_NAME
FROM
INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS Tab
INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS Col ON
Col.Constraint_Name = Tab.Constraint_Name AND
Col.Table_Name = Tab.Table_Name
WHERE
Constraint_Type = 'PRIMARY KEY' AND
Col.Table_Name = #TableName AND
Col.TABLE_SCHEMA = #SchemaName
Once you see the result, you can drop the current primary key with another ALTER TABLE:
ALTER TABLE Cars2.dbo.Cartable1 DROP CONSTRAINT ConstraintNameFromThePreviousQuery
CREATE TABLE Cartable1(
LicensePlate int NOT NULL PRIMARY KEY,
Manufacturer varchar(255) NOT NULL,
Model varchar(255),
Colour varchar(255),
Model varchar(255),
Year int,
EngineSize int,
Value float
);
You do this when you create a table or through an alter table statement:
create table [Cars2].[dbo].[Cartable1] (
LicensePlace varchar(?) primary key,
. . .
);
I recommend adding the primary key when you create the table and before you add any data into it.
If You already have table with data try this ,
ALTER TABLE [Cars2].[dbo].[Cartable1]
ADD CONSTRAINT PRIMARY_KEY_LicensePlate PRIMARY KEY(LicensePlate)

Getting Foreign Key Ids from Primary Key

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

How to join in sql with multiple tables (need) join multiple references?

Some table only have identity columns but this is because this is a example only.
create table producto(
idproducto int identity primary key
)
create table inventario(
idInventario id int identity primary key,
idproducto int,
foreign key (idproducto) references producto(idproducto)
)
create table sucursal(
idSucursal id int identity primary key
)
create table inventariosucursal(
idinventariosucursal id int primary key,
idsucursal int,
idinventario int,
foreign key (idsucursal) references sucursal (idsucursal),
foreign key (idinventario) references inventario (idinventario),
)
create table mejoresventasxsucursal
(
mejoresventasxsucursal id int primary key,
idsucursal int,
idproducto int,
foreign key (idsucursal) references sucursal(idsucursal),
foreign key (idproducto) references producto (idproducto)
)
What do I need?
select the stocking ( inventario table ) by every branch office (sucursal table) but only most selled products (mejoresventasxsucursal table).
the problem is mejoresventasxsucursal table does "join" with 2 tables product and sucursal but after i have other table with does "join" with same 2 tables product and sucursal.
select s.id,i.id,p.id,
from producto p
join inventario i on p.idproducto = i.idproducto
join inventariosucursal i_s on i_s.idinventario = i.idinventario
join sucursal s on i_s on i_s.idsucursal = s.idsucursal
join producto p on i.producto = p.idproducto
join mejoresventasxsucursal mv on mv.idsucursal = s.idsucursal
Here is the problem. I need join mejoresventasxsucursal with producto. I know I could do something as join producto p2 but I would like to know what is the best way for join with producto. I know other solution is in where condition doing where p.idproducto=mv.idprocucto

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