How cascade Update/Delete works internally in SQL Server? - sql-server

Ok, I believe the question was not clear. Here i rewrite this in other way.
Suppose i create two tables,
table1(c1 int PRIMARY KEY)
table2(table1c11 int)
There is a relation between table1 and table2
i.e. table1.c1=table2.table1c11
And, i execute the following statement in the table1 and table2
insert into table1(c1)
values('a'),('b'),('c'),('d'),('e')
insert into table2(table1c11)
values('a'),('a'),('b'),('d')
And now what I want to achieve is that, once I update the value of c1 in table1 the corresponding data in table2 gets changed automatically. For this I need to create the constraint in table1 and table2 relationships and apply the CASCADE UPDATE.
So, later I apply a new SQL update statement in table1 i.e.
Update table1 set c1=c1+'updated'
Then the data in table2 gets changed also, But what if I want to achieve the same functionality via INSTEAD OF UPDATE TRIGGER, then I need to write the instead of update trigger and inside that, I need to handle that with two magic tables INSERTED and DELETED.
But the main point is that, in this case, I have only one column present in the table1 and I am updating that same column, so how could i map the inserted and deleted rows. Same thing is being done by the SQL Server as well if I use CASCADing.
So, the question arises how SQL Server handles batch update in case of the primary key data changes in the table.

So, the question arises how SQL Server handles batch update in case of
the primary key data changes in the table.
SQL Server builds a query plan for the update statement that update both tables.
Create the tables:
create table T1
(
T1ID int primary key
);
create table T2
(
T2ID int primary key,
T1ID int references T1(T1ID) on update cascade
)
Add some data:
insert into T1 values(1), (2)
insert into T2 values(1, 1), (2, 1), (3, 2)
Update primary key of T1:
update T1
set T1.T1ID = 3
where T1.T1ID = 1
The query plan for the update looks like this:
The plan has two Clustered Index Update steps, one for T1 and one for T2.
Update 1:
How does SQL Server keep track of the rows to update when more than one primary key value is updated?
update T1
set T1.T1ID = T1.T1ID + 100
The Eager Spool in the top branch (update of T1) saves the old T1ID and the new calculated T1ID (Expr1013) to a temporary table that is used by the lower branch (update of T2). The Hash Match in the lower branch is joining the Table Spool with T2 on the old T1ID. Output from the Hash Match to the update of T2 is T2ID from the Clustered Index Scan of T2 and the new calculated T1ID (Expr1013) from the Table Spool.
Update 2:
If you need to replace the cascade update with a instead of trigger you need to have a way to join the inserted and deleted tables in the trigger. That can be done with a surrogate key in T1.
Tables:
create table T1
(
T1ID int primary key,
ID int identity unique
);
create table T2
(
T2ID int primary key,
T1ID int references T1(T1ID)
);
The trigger could look like this.
create trigger tr_T1 on T1 instead of update as
insert into T1(T1ID)
select T1ID
from inserted;
update T2
set T1ID = I.T1ID
from inserted as I
inner join deleted as D
on I.ID = D.ID
where D.T1ID = T2.T1ID;
delete from T1
where T1ID in (
select T1ID
from deleted
);
SQL Fiddle

Related

Copy IDENTITY column from another table and generate new IDs for missing records

I'm trying to copy identity column from one table to another, but I'm getting this error:
Cannot update identity column 'ID'.
I tried following code:
ALTER TABLE [dbo].[TableA]
ADD [ID] INT IDENTITY(20000,1), -- MAX(TableB.Id) < 20000
SET IDENTITY_INSERT TableA ON
UPDATE TableA
SET TableA.[ID] = TableB.[ID]
FROM TableA
INNER JOIN TableB ON TableB.ID = TableA.ID
SET IDENTITY_INSERT TableA OFF;
Scenario
I have two tables with 1:0-1 relationship.
TableA: Code (PK)
A,
B,
C,
D
TableB: Id (PK), Code (Unique)
1, A
2, B
3, D
Question
How to
Create new identity column TableA.Id
Copy values from TableB.Id
Ensure new unique values for TableA.Id if it was missing in TableB.Id
=NOT A REAL ANSWER TO THE ORIGINAL QUESTION, see comment=
You can not update a column which in itself is your join criteria. This just ain't work.
The safest way to keep the identity column intact is to delete rows from Table B in Table A first.
Then just insert from Table B into Table A with IDENTITY_INSERT ON.
Also this assumes there is a 1:1 relationship between the rows common to Table A and Table B. A 1:0-1 relationship between Table A and B means you need to capture ID from Table B in Table A as a foreign key relationship and separate column instead if this can lead to NULL results in Table A.
So in a nutshell (pseudo code) with assumption of 1:1 relationship between the rows:
DELETE FROM Table A WHERE Key EXISTS IN (SELECT Key FROM Table B)
;
SET IDENTITY_INSERT Table A ON
;
INSERT INTO Table A
( id_column, [other_columns [, othercolumns] ] )
SELECT
id_column, [other_columns [, othercolumns] ]
FROM Table B
;
SET IDENTITY_INSERT Table A OFF
;

Duplicates not getting ignored in SQL Server

I have a temp table that has two rows.
Their Id is 999359143, 999365081
I have a table that doesn't have a primary key but has a unique index based off of the id and date.
This 999359143 already exists in the table. So when I use my query it still is trying to insert the row from the temp table into the normal table and it errors. This is the query below
INSERT INTO [XferTable]
([DataDate]
,[LoanNum]
)
SELECT Distinct t1.[DataDate]
,t1.[LoanNum]
FROM #AllXfers t1 WITH(HOLDLOCK)
WHERE NOT EXISTS(SELECT t2.LoanNum, t2.DataDate
FROM XferTable t2 WITH(HOLDLOCK)
WHERE t2.LoanNum = t1.LoanNum AND t2.DataDate = t1.DataDate
)
Is there a better way to do this?
You should use the MERGE statement, which acts atomically so you shouldn't need to do your own locking (also, isolation query hints on temporary tables doesn't achieve anything).
MERGE XferTable AS SOURCE
USING #AllXfers AS TARGET
ON
SOURCE.[DataDate] = TARGET.[DataDate]
AND SOURCE.[LoanNum] = TARGET.[LoanNum]
WHEN NOT MATCHED BY TARGET--record in SOURCE but not in TARGET
THEN INSERT
(
[DataDate]
,[LoanNum]
)
VALUES
(
SOURCE.[DataDate]
,TARGET.[LoanNum]
);
Your primary key violation is probably because you are using (Date, Loan#) as the uniqueness criteria and your primary key is probably only on Loan#.

I need a function which will insert rows in multiple tables

postgresql
So i am inserting a row in a table1 and this row contains one field which is a field in table2. So i would like to create a function which will insert a row in table2 when i am inserting a row in table1.
So example:
I have two tables
table1
....
....
....
table2
....
....
....
I insert in table1
Insert in table1 values ("Sam","USA");
as a result i want to have
table1
Sam Usa
...
...
...
table2
Usa ...
...
...
So what function should i write and what trigger? Also if there is a row in table2 which has a field USA, this function should not insert one more row with USA
Sorry, if i explained it to complicated
Like #Jorge Campos mentioned in comment duplication of data is usually bad idea.
But if you are really have some scenario where you need to do this you need to create trigger on source table and insert data in destination table with existing check. Here is example in SQL Server:
CREATE TABLE Tbl1(
Id INT NOT NULL IDENTITY PRIMARY KEY,
Name NVARCHAR(100) NOT NULL
)
GO
CREATE TABLE Tbl2(
Id INT NOT NULL IDENTITY PRIMARY KEY,
Name NVARCHAR(100) NOT NULL
)
GO
CREATE TRIGGER Sync ON Tbl1 AFTER INSERT AS
INSERT INTO Tbl2 (Name)
SELECT src.Name FROM inserted src
LEFT JOIN Tbl2 dst ON src.Name = dst.Name
WHERE dst.Id IS NULL
GO
INSERT INTO Tbl1 (Name) VALUES ('STR')
SELECT * FROM Tbl2

SQL Server DB Trigger

I need to insert a row into a table if a particular row in another table is updated.
How do I do an IF statement in the the DB trigger on Table1, saying if Table1.column1 = 'TC' then INSERT a row in Table2.
You would do this in an update trigger on the 'other' table.
There are two special tables in triggers: inserted and deleted. You join these two tables in such a way that the result set is the rows you wish to insert. Ergo -
create trigger [after_update_on_Table1] on [Table1] for update
as
...
insert
into [Table2] (...)
select
...
from
inserted as i
inner join
deleted as d
on (i.<*pk*> = d.<*pk*>)
where
<*other conditions if applicable*>
...
<pk> is whatever the appropriate primary key would be. If this is a compound primary key then AND together the different primary key components.
For what you describe thus far you do not require an if statement.

Help on SQL Server trigger

Suppose I have 3 tables
t1
Nid name
1 aaa
2 bbb
3 ccc
delT1
Nid name
t2
Sid Nid value
1 1 AAA
2 1 BAC
3 2 CSA
In table t1 Nid is primary key which is foreign key for t2
Now what I want is when I delete value from t1 it automatically deletes all values from t2 where t1.Nid=t2.Nid and a value of deleted t1 get inserted into delT1
How can I create a trigger for this type of task?
Please help me since I am new to sql
A normal trigger wouldn't work: the foreign key would give an error before the code runs.
What you can do is set a CASCADE on your foreign key so a delete in T1 will delete from T2 automatically.
Personally, I'd use a stored proc and transaction to delete from T2 first, then T1.
Modify the FK in T2 to be ON DELETE CASCADE:
ALTER TABLE T2 DROP CONSTRAINT FK_T1_Nid; <-- your constraint name here
ALTER TABLE T2 ADD CONSTRAINT FK_T1_Nid FOREIGN KEY (Nid)
REFERENCES T1 (Nid) ON DELETE CASCADE;
Then create a trigger on T1 to push the information to delT1:
CREATE TRIGGER TR_T1_D ON T1 FOR DELETE
AS
SET NOCOUNT ON;
INSERT delT1
SELECT Nid, Name
FROM Deleted;
Note this trigger prevents you from using an OUTPUT clause on DELETEs against T1. BOL says:
If the OUTPUT clause is specified without also specifying the INTO keyword, the target of the DML operation cannot have any enabled trigger defined on it for the given DML action. For example, if the OUTPUT clause is defined in an UPDATE statement, the target table cannot have any enabled UPDATE triggers.
#gbn has the preferred way to go. Since you asked for a trigger, you could do this for comparison's sake:
CREATE TRIGGER t1_Delete ON t1
INSTEAD OF DELETE AS BEGIN SET NOCOUNT ON;
INSERT INTO delT1 (Nid, name)
SELECT Nid, name
FROM DELETED;
DELETE FROM t2
WHERE t2.Nid IN (SELECT Nid FROM DELETED);
DELETE FROM t1
WHERE t1.Nid IN (SELECT Nid FROM DELETED);
END;

Resources