5

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.

Mikael Eriksson
  • 136,425
  • 22
  • 210
  • 281
Manish Rawat
  • 1,142
  • 1
  • 18
  • 34
  • Are you allowed to change the structure of the tables (I mean table2)? – ypercubeᵀᴹ Feb 24 '13 at 18:17
  • yes, i can, but the thing is that, there are couple of columns which are there in table2 and those columns i need in the next table you create. So, i can create the table3 but table3 should reference table2 instead of table1. – Manish Rawat Feb 25 '13 at 06:21

1 Answers1

13

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:

query plan for update with on update cascade

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

query plan for update with on update cascade more than one row

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

Mikael Eriksson
  • 136,425
  • 22
  • 210
  • 281
  • Can you extract the information from the Query Plan and suggest me the trigger for that. It would be helpful for me. – Manish Rawat Feb 25 '13 at 11:09
  • In your update statement you are only updating the one record, which is quite quite easily mapped in the inserted/deleted magic tables. I believe if you do the batch update like UPDATE T1 SET T1.T1ID=T1.T1ID+100 then it would be very good example then and there i can actually understand the concepts behind the SQL Server internal cascading. Thanks in advance for your time. – Manish Rawat Feb 25 '13 at 11:20
  • @ManishRawat Using a update statement that modifies more than one row does not change the query plan all that much, try it and see. You asked about how SQL Server does the update and this is how it is done. Are you also asking about how to do the cascade update with a trigger instead? – Mikael Eriksson Feb 25 '13 at 12:02
  • yes, i need a similar approach using the instead of trigger because sql server is also doing the same thing internally but i don't know how. – Manish Rawat Feb 25 '13 at 12:27
  • is there any hidden rowid in the inserted/updated table by which i can join these two tables? – Manish Rawat Feb 25 '13 at 12:28
  • 2
    @ManishRawat nope, there is not. I don't think there is any way to do this in a trigger unless you could add some kind of surrogate key in `T1` that you can use as a join between `inserted` and `deleted`. It could be an `identity` column with a unique constraint. Identity columns does not have to be the primary key. `create table T1 ( T1ID int primary key, ID int identity unique );` – Mikael Eriksson Feb 25 '13 at 12:39
  • Ok, if its not possible then how SQL Server is doing this internally? – Manish Rawat Feb 25 '13 at 16:36
  • @ManishRawat added some explanation on the multi PK update query plan. – Mikael Eriksson Feb 26 '13 at 06:50
  • I believe, you are just telling me the sql plan, i am only interested in the same functionality in the INSTEAD OF TRIGGER. Consider a scenario where i have a huge database with 100's of tables and while investigating you found that one relation don't have a foreign key relationship, and while adding a relation between them you found that the Cascading cannot be done in the relation and only instead of trigger is the option in order to the update of the foreign key data. Please suggest the similar approach without altering the database table itself. – Manish Rawat Feb 26 '13 at 15:56
  • 3
    @ManishRawat Yes, the SQL plan is what is telling you how SQL Server internally does this, I think that is what this question is about. There is no way to do this in a trigger without modifying your tables. You have three ways to fix your situation. **1** Delete all rows in `T2` that does not have valid FK to `T1`. **2** Add all the necessary rows to `T1` so that all rows in `T2` has a valid FK. **3** Allow null values in the FK in `T2` and update the FK in `T2` to null for all rows that does not have a valid FK to `T1`. – Mikael Eriksson Feb 27 '13 at 06:56
  • my question remains unanswered as i am very much interested in the query which sql server internally fires in order to the same thing which you are saying we cannot do – Manish Rawat Feb 27 '13 at 16:27
  • @ManishRawat SQL does not "fire a query internally". It does what the query plan tell you it does. – Mikael Eriksson Feb 27 '13 at 20:16