Manually delete cascade on SQL Server - sql

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

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)

How to update foreign keys in single one statement

I want to update foreign keys into a table named User where foreign keys refer to a Project table, a Group table.
The user belongs to a project, within a group, the group are defined to the project level. The detailed user information are also stored into a table ToolUser
The create statement for the User table is as following:
CREATE TABLE [dbo].[User](
[ID] [int] IDENTITY(1,1) NOT NULL,
[IDproject] [int] NOT NULL,
[IDtoolUser] [int] NOT NULL,
[IDgroup] [int] NOT NULL,
[datemodified] [datetime] NOT NULL DEFAULT (getdate())
IDproject is FK for ID in Project table
IDtoolUser is FK for ID in ToolUser table
IDgroup is FK for ID in Group table
The update I want to perform consist in changing the group the user is assigned to which requires to resolve the project ID, the group ID before updating the groupID in the User table
I came up with a solution where the group id is calculed with a function, [dbo].[IDofGroup], and used into an UPDATE statement as following
UPDATE [dbo].[User]
SET IDgroup = [dbo].[IDofGroup]('TESTPROJ','none')
FROM [dbo].[User] as u
INNER JOIN [dbo].[Group] g
ON g.[ID]= u.[IDgroup]
INNER JOIN [dbo].[Project] p
ON p.[ID]= u.[IDproject]
AND p.[name] ='TESTPROJ'
WHERE g.[name] = 'TESTGROUP'
GO
The function IDofGroup itself performs couple SELECT to resolve project ID and group ID as following
ALTER FUNCTION [dbo].[IDofGroup]
(
-- Add the parameters for the function here
#ProjectName as varchar(max),
#GroupName as varchar(max)
)
RETURNS int
AS
BEGIN
-- Declare the return variable here
DECLARE #ProjectID int, #ResultID int
SET #ResultID=0
-- get project ID for #ProjectName
SELECT #ProjectID= ID
FROM [dbo].[Project]
WHERE [name]=#ProjectName
-- find id fro group which name is #GroupName and project ID #ProjectID
SELECT #ResultID= ID
FROM [dbo].[Group]
WHERE [IDproject]=#ProjectID
AND [name]=#GroupName
-- Return the result of the function
RETURN #ResultID
END
I m wondering if there any way to perform the UPDATE with some kind of JOIN in a single UPDATE statement w/o the need of using of function IDofGroup or other SELECT statements before the UPDATE statement
Try this one:
UPDATE u
SET IDgroup = g1.[ID]
FROM [dbo].[User] as u
INNER JOIN [dbo].[Project] p
ON p.[ID]= u.[IDproject]
INNER JOIN [dbo].[Group] g
ON g.[ID]= u.[IDgroup]
INNER JOIN [dbo].[Group] g1
ON g.[IDproject] = g1.[IDproject]
WHERE p.[name] ='TESTPROJ'
AND g.[name] = 'TESTGROUP'
AND g1.[name] = 'none'

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)

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);