SQL Server DB Trigger - sql-server

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.

Related

Insert/Update, ignore rows which would violate FK

If someone performs an update or insert on a table which has a foreign key to another, if a nonexistent value appears, an error is thrown. Is there an automated enough way to just ignore the faulty columns and continue with the others?
I can only think of an instead-of trigger, but it sounds messy.
You should check the rows in your INSERT query, by joining the source data with the referenced table.
For example, let's suppose that you have the following data in the SourceData table and you want to insert it in the Sales.CurrencyRate table in the AdventureWorks2019 database:
CREATE TABLE SourceData (
CurrencyRateDate datetime,
FromCurrencyCode char(3),
ToCurrencyCode char(3),
PRIMARY KEY (CurrencyRateDate, FromCurrencyCode, ToCurrencyCode),
Rate money NOT NULL
)
INSERT INTO SourceData (CurrencyRateDate, FromCurrencyCode, ToCurrencyCode, Rate) VALUES
('20200429','EUR','RON',4.8425),
('20200429','EUR','ROL',48425),
('20200430','EUR','RON',4.8421),
('20200430','EUR','ROL',48421)
INSERT INTO Sales.CurrencyRate (CurrencyRateDate, FromCurrencyCode, ToCurrencyCode, AverageRate, EndOfDayRate)
SELECT sd.CurrencyRateDate, sd.FromCurrencyCode, sd.ToCurrencyCode, sd.Rate, sd.Rate
FROM SourceData sd
If you simply run the INSERT statement mentioned above, you would get an error saying "The INSERT statement conflicted with the FOREIGN KEY constraint "FK_CurrencyRate_Currency_ToCurrencyCode". The conflict occurred in database "AdventureWorks2019", table "Sales.Currency", column 'CurrencyCode'.", because the currency "RON" is not present in the Sales.Currency table.
To avoid this error and insert only the data that has corresponding rows in the referenced table, you would simply use a JOIN for each FK, like this:
INSERT INTO Sales.CurrencyRate (CurrencyRateDate, FromCurrencyCode, ToCurrencyCode, AverageRate, EndOfDayRate)
SELECT sd.CurrencyRateDate, sd.FromCurrencyCode, sd.ToCurrencyCode, sd.Rate, sd.Rate
FROM SourceData sd
INNER JOIN Sales.Currency c1 ON c1.CurrencyCode=sd.FromCurrencyCode
INNER JOIN Sales.Currency c2 ON c2.CurrencyCode=sd.ToCurrencyCode

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#.

Get Primary Key from insert

I'm attempting to use SSIS to do a data migration from an old system to a new system. I have the tables TableA, TableB and OldTable. The tables are as follows:
TableA
ID
BusinessTypeEnum
Other
TableB
ID
TableB_ID (FK)
PermitNumber
OldTable
ID
BusinessType
PermitNumber
As you can see, TableA and TableB (which are in the new system) are really OldTable separated out into two tables.
My problem is that in order to insert a record in TableB I need to know that PrimaryKey that was assigned at the time the corresponding records was inserted in TableA.
I had originally planned on doing a LookUp when attempting to insert records into table TableB however, there isn't any data stored in TableA that would allow me to return.
Any ideas?
It sounds like you're using a single data flow: source -> split -> two destinations (TableA, TableB).
What if you divided the process into two data flows?
The first simply pulls from OldTable and loads into TableA.
The second--loading into TableB--again pulls from OldTable but also gets the relevant identity value from TableA by doing a Merge Join to, or a Lookup from, that table.
(Note: the second flow needs to be set to run only after the first completes via a precedence constraint as it relies on OldTable's data being inserted into TableA before its run.)
You can use the OUTPUT Clause in our insert statement to return the value of the identity column.
INSERT TableA (BusinessTypeEnum, Other)
OUTPUT INSERTED.ID
VALUES ('abc', 'def')
in addition to inserting a record, the above will act like a select.
I should mention that there is an INTO clause in the OUTPUT clause that can be used with a batch insert. The above example is for a single record insert.

How cascade Update/Delete works internally in 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

How to insert a new record to table A when table A deppends on table B and vice versa

I'm not sure if this is well designed, if it's not please, advice me on how to do this.
I'm using Sql Server 2008
I have:
TableA (TableA_ID int identity PK, Value varchar(10), TableB_ID PK not null)
TableB (TableB_ID int identity PK, Value varchar(10), TableA_ID PK not null)
The goal is simple:
TableA can have rows only if there is at least 1 row in TableB associated with TableA;
And for each row in TableB, there must be a row associated with it in TableA);
TableA is the "Parent Table", and TableB is the "Children's table", it's something like, a parent should have 1 or more children, and each child can have only 1 parent.
Is this right?
The problem I'm having is when I try to do an INSERT statement, if this is correct, how should I make the INSERT? temporary disable the constraints?
Thanks!
The problem I'm having is when I try to insert
TableA (TableA_ID int identity PK, Value varchar(10))
TableB (TableB_ID int identity PK, Value varchar(10), TableA_ID not null)
as a parent, table a does not need to reference table b, since table be requires there be a field in table a. this is called a one to many relationship.
so in table a you might have these values:
1 a
2 b
3 c
and in table b you could have these:
1 asdf 1
2 sdfg 1
3 pof 2
4 dfgbsd 3
now you can make a query to show the data from table a with this:
select b.TableB_ID, b.Value, a.TableA_ID, a.Value
from TableB b
inner join TableA
on b.TableA_ID=a.TableA_ID
The parents don't depend on the children. You need to remove your reference to Table B in Table A.
You have a circular dependency. These don't really work well for declarative enforcement, you would have to disable the constraints every time you wanted to insert.
That's an unusual requirement. If I was stuck with it (and I would really push back to make sure it was indeed a requirement) I would design it this way:
Make a regular foreign key from table a to table b with a the parent and b the child.
Add a trigger to table a that inserts a record to table b if one does not exist when a table a record is inserted. Add another trigger to table b that deletes the table a record if the last related record in table b is deleted.
ALternatively, you could put the inserts to both tables ina stored proc. Remove all insert rights to the table except through the proc. YOu would still need the foreign key relationship from tablea to table b and the trigger on table b to ensure that if the last record is deleted the table a record is deleted. But you could do away with the trigger on table a in this case.
I would use the first scenario unless there is information in table b that cannot be found from the trigger on table a, say one or more required fields that don't have a value you can figur eout form table a.
I would put the inserts into a proc: disable the constraints, insert the data, enable the constraints. You may need to make sure that this is the only transaction going on whilst the constraints are disabled though.
That could be acheived by making the isolation level SERIALIZABLE, but that in turn could massace your concurrency.
Kev

Resources