MS SQL IF with a select value - sql-server

How do I state - If a field in my table is NULL only then do the update.
For example:
IF customer_day_phone (from my #invoice table) where id_key = #id_key -matches my parameter - is null. Then run my update.
But i'll have multiple rows coming back and I only want to update those who are NULL.
IF select customer_day_phone from #invoice where id_key = #id_key and customer_day_phone is null
BEGIN
...
END
I need the IF statement I can't simply use where day_phone is NULL because its a two part update. The first part updates the value if the field is null the second update formats the data (but I don't want it formatted if it wasn't updated only if it was).

I dont see any reason why you couldn't simply do TWO PART update in a single update statement.
Simply do the following. Update to the "Formatted" value in your first update and avoid running another update statement, just to update it first and then format it.
UPDATE #invoice
SET columnName = 'value'
WHERE customer_day_phone IS NULL --<-- this will only bring nulls
AND id_key = #id_key
Edit
From your update statement I think it should be as simple as .....
update a
set a.customer_day_phone = ISNULL(b.phone,'') + ' ' + ISNULL(customer_day_phone,'')
from #invoice a
join T_PHONE b on a.customer_no = b.customer_no
where b.[type] = 5
and a.customer_day_phone IS NULL
-- and id_key = #id_key --<-- you had this in your first query too

Let me guess maybe it something like this ?
Update #invoice set <fields which you want to update> WHERE id_key = #id_key and customer_day_phone is null
And at this point you dont need IF Statement

Assuming you want to exclude the record update entirely...
UPDATE invoice SET customer_Day_Phone = #InputValue
WHERE customer_day_Phone is null
and id_key = #Id_Key
or if you need to update other values on the record but not phone..
UPDATE invoice SET customer_Day_Phone = case
when customer_Day_Phone is null then #InputValue
else customer_Day_phone end,
field2=#field2value
WHERE id_key = #Id_Key

Related

Check if a products date is overdue

I want to create a kind of check, thats check if a products date is overdue. In my product table I have a column named veilingGesloten (auctionClosed) that can contain the value 'yes' or 'no'.
So I made this:
create TRIGGER [dbo].[trg_validategesloten]
ON [dbo].[voorwerp]
after update, insert
AS
begin
set nocount on;
update [dbo].[voorwerp]
set veilinGesloten =
case when ( inserted.looptijdeindeDag <= GETDATE() ) then 'wel'
else 'niet'
end
from [dbo].[voorwerp]
inner join inserted on [dbo].[voorwerp].voorwerpnummer = inserted.voorwerpnummer;
end
Now im wondering how to actually make this an auto process without any insert or update events
You don't trigger it - you use a computed column:
alter table voorwerp add veilinGesloten
as (case when looptijdeindeDag <= getdate() then 'wel' else 'niet' end);
Personally I would use a bit field and convert to meaningful words at the front-end.
alter table voorwerp add veilinGesloten
as (convert(bit, case when looptijdeindeDag <= getdate() then 1 else 0 end));

Looping table valued parameter values passed as parameter to stored procedure for checking a value

I have a stored procedure that receives a TVP as input. Now, I need to check the received data for a particular ID in a primary key column. If it exists, then I just need to update the table using those new values for other column (sent via TVP). Else, do an insert.
How to do it?
CREATE PROCEDURE ABC
#tvp MyTable READONLY
AS
IF EXISTS (SELECT 1 FROM MYTAB WHERE ID= #tvp.ID)
DO update
ELSE
Create
Just wondering the if exists loop I did is correct. I reckon its wrong as it will only check for first value and then update. What about other values? How should I loop through this?
Looping/CURSOR is the weapon of last resort, always search for solution that is SET based, not ROW based.
You should use MERGE which is designed for this type of operation:
MERGE table_name AS TGT
USING (SELECT * FROM #tvp) AS SRC
ON TGT.id = SRC.ID
WHEN MATCHED THEN
UPDATE SET col = SRC.col
WHEN NOT MATCHED THEN
INSERT (col_name, col_name2, ...)
VALUES (SRC.col_name1, SRC.col_name2, ...)
If you don't like MERGE use INSERT/UPDATE:
UPDATE table_name
SET col = tv.col,
...
FROM table_name AS tab
JOIN #tvp AS tv
ON tab.id = tv.id
INSERT INTO table_name(col1, col2, ...)
SELECT tv.col1, tv.col2, ...
FROM table_name AS tab
RIGHT JOIN #tvp AS tv
ON tab.id = tv.id
WHERE tab.id IS NULL

Insert Multiple Rows in SQL SERVER using a Trigger

I need to insert multiple rows into another table using a trigger, but it only inserts the last record
I have checked some other posts in stackoverflow and didn't find an Answer,
This is my trigger
IF(#TNAEventID IN(1,2,3))
BEGIN
INSERT INTO [Biostar].Cen.WentOutLog (AutoID, nUserID, nOutDateTime,nOutTNAEvent ,nReaderID) values (#AutoID,#UseID, #DateTime,#TNAEventID, #ReaderID )
END
else IF(#TNAEventID=0)
BEGIN
UPDATE Cen.WentOutLog Set nINDateTime =#DateTime,nInTNAEvent = #TNAEventID Where AutoID = (Select top (1) AutoID from Cen.WentOutLog where nINDateTime is null AND nOutDateTime<#DateTime AND nUserID=#UseID order by nOutDateTime desc)
END
else
begin
....
end
Thanks in Advance.
You can try below code, Insert code is readily usable.
You might need to change the UPDATE statement as I do not know what is
your data:
INSERT INTO [Biostar].[Cen].[WentOutLog]
([AutoID], [nUserID], [nOutDateTime], [nOutTNAEvent], [nReaderID])
SELECT [AutoID], [nUserID], [nOutDateTime], [nOutTNAEvent], [nReaderID]
FROM INSERTED
WHERE TNAEventID IN (1, 2, 3)
UPDATE W
FROM [Cen].[WentOutLog]
SET W.[nINDateTime] = I.[DateTime],
W.[nInTNAEvent] = I.[TNAEventID]
INNER JOIN INSERTED I ON W.[AutoID] = I.[AutoID]
WHERE I.[TNAEventID] = 0
AND W.[nINDateTime] IS NULL
AND W.[nOutDateTime] < I.[DateTime]
AND W.[nUserID] = I.[UserID]

Unable to set value based on INSERTED table in trigger

GOAL:I'm creating after insert trigger that should insert new record to OrderSuspendRule table based on rule in this table that was related with Promotion of which new version was created.
PROBLEM
I cannot set value to #SUS_ID. Select returns value but it isn't set to variable.
Sample insert:
INSERT INTO PromotionHeader (Guid,CreatedAt,UpdatedAt,IsActive,CompanyId,UpdatedById,CreatedById,Name,[Description],ValidFrom,ValidTo,BusinessUnitId,OfferId,[Version],StatusId,PreviousId)
select newid(),CreatedAt,UpdatedAt,1,CompanyId,UpdatedById,CreatedById,Name,[Description],ValidFrom,ValidTo,BusinessUnitId,OfferId,[Version]+1,StatusId,916 FROM PromotionHeader WHERE Id=916
Where PreviousId points to older version of promotion.
CREATE TRIGGER TRIG1 ON DBO.PromotionHeader
AFTER INSERT
AS
DECLARE #SUS_ID INT
SET #SUS_ID = (
SELECT Max(id)
FROM OrderSuspendRule
WHERE PromotionHeaderId = (
SELECT PreviousId
FROM inserted
WHERE ID = SCOPE_IDENTITY()
)
AND ISACTIVE=1
)
IF (#SUS_ID IS NOT NULL) --**VARIABLE IS ALWAYS NULL NO MATTER WHAT**
BEGIN
INSERT INTO OrderSuspendRule (
Guid
,CreatedAt
,UpdatedAt
,IsActive
,CompanyId
,UpdatedById
,CreatedById
,SuspendFrom
,SuspendTo
,PromotionHeaderId
,SuspendTypeId
,OfferItemId
)
SELECT NEWID()
,GETDATE()
,GETDATE()
,1
,CompanyId
,UpdatedById
,CreatedById
,SuspendFrom
,SuspendTo
,SCOPE_IDENTITY()
,SuspendTypeId
,OfferItemId
FROM OrderSuspendRule
WHERE id = #SUS_ID
END
Inside a for insert trigger, you can assume that all rows in the inserted table were inserted. There is no need to double check this with scope_identity().
To explain why scope_identity() is null, remember that scope_identity() returns the last inserted identity in the current scope. Since your trigger runs in its own scope, this will always be null, unless the trigger itself performs an insert.
Also, be aware that your trigger can be run for an insert of multiple rows. That means you can't expect only a single #sus_id, there might be many.

Determine Old primary key in a SQL Trigger

I've done this before somewhere I'm sure of it!
I have a SQL Server 2000 table that I need to log changes to fields on updates and inserts into a second Logging table. A simplified version of the structure I'm using is below:
MainTable
ID varchar(10) PRIMARY KEY
DESCRIPTION varchar(50)
LogTable
OLDID varchar(10)
NEWID varchar(10)
For any other field something like this would work great:
Select i.DESCRIPTION As New, d.DESCRIPTION As Old
From Inserted i
LEFT JOIN Deleted d On i.ID=d.ID
...But obviously the join would fail if ID was changed.
I cannot modify the Tables in way, the only power I have in this database is to create a trigger.
Alternatively is there someone who can teach me time travelling and I'll go back into the past and ask myself back then how I did this? Cheers :)
Edit:
I think I need to clarify a few things here. This is not actually my database, it is a pre-existing system that I have almost no control of, other than writing this trigger.
My question is how can I retrieve the old primary key if said primary key was changed. I don't need to be told that I shouldn't change the primary key or about chasing up foreign keys etc. That's not my problem :)
DECLARE #OldKey int, #NewKey int;
SELECT #Oldkey = [ID] FROM DELETED;
SELECT #NewKey = [ID] FROM INSERTED;
This only works if you have a single row. Otherwise you have no "anchor" to link old and new rows. So check in your trigger for > 1 in INSERTED.
Is it possible to assume that the INSERTED and DELETED tables presented to you in a trigger are guaranteed to be in the same order?
I don't think it's possible. Imagine if you have 4 rows in the table:
1 Val1
2 Val2
3 Val3
4 Val4
Now issue the following update:
UPDATE MainTable SET
ID = CASE ID WHEN 1 THEN 2 WHEN 2 THEN 1 ELSE ID END
Description = CASE ID WHEN 3 THEN 'Val4' WHEN 4 THEN 'Val3' ELSE Description END
Now, how are you going to distinguish between what happened to rows 1 & 2 and what happened to rows 3 & 4. And more importantly, can you describe what's different between them? All of the stuff that tells you which columns have been updated won't help you.
If it's possible in this case that there's an additional key on the table (e.g. Description is UNIQUE), and your update rules allow it, you could write the trigger to prevent simultaneous updates to both keys, and then you can use whichever key hasn't been updated to correlate the two tables.
If you must handle multiple-row inserts/updates, and there's no alternate key that's guaranteed not to change, the only way I can see to do this is to use an INSTEAD OF trigger. For example, in the trigger you could break the original insert/update command into one command per row, grabbing each old id before you insert/update.
Within triggers in SQL Server you have access to two tables: deleted and inserted. Both of these have already been mentioned. Here's how they function depending on what action the trigger is firing on:
INSERT OPERATION
deleted - not used
inserted - contains the new rows being added to the table
DELETE OPERATION
deleted - contains the rows being removed from the table
inserted - not used
UPDATE OPERATION
deleted - contains the rows as they would exist before the UPDATE operation
inserted - contains the rows as they would exist after the UPDATE operation
These function in every way like tables. Therefore, it is entirely possible to use a row based operation such as something like the following (Operation exists only on the audit table, as does DateChanged):
INSERT INTO MyAuditTable
(ID, FirstColumn, SecondColumn, ThirdColumn, Operation, DateChanged)
VALUES
SELECT ID, FirstColumn, SecondColumn, ThirdColumn, 'Update-Before', GETDATE()
FROM deleted
UNION ALL
SELECT ID, FirstColumn, SecondColumn, ThirdColumn, 'Update-After', GETDATE()
FROM inserted
----new----
add an identity column to the table that the application can not change, you can then use that new column to join the inserted to the deleted tables within the trigger:
ALTER TABLE YourTableName ADD
PrivateID int NOT NULL IDENTITY (1, 1)
GO
----old----
Don't ever update/change key values. How can you do this and fix all of your foreign keys?
I wouldn't recommend ever using a trigger that can't handle a set of rows.
If you must change the key, insert a new row with the proper new key and values, use SCOPE_IDENTITY() if that is what your are doing. Delete the old row. Log for the old row that it was changed to the new row's key, which you should now have. I hope there is no foreign key on the changed key in your log...
You can create a new identity column on table MainTable (named for example correlationid) and correlate inserted and deleted tables using this column.
This new column should be transparent for existing code.
INSERT INTO LOG(OLDID, NEWID)
SELECT deleted.id AS OLDID, inserted.id AS NEWID
FROM inserted
INNER JOIN deleted
ON inserted.correlationid = deleted.correlationid
Pay attention, you could insert duplicate records in the log table.
Of course nobody should be changing the primary key on the table -- but that is exactly what triggers are supposed to be for (in part), is to keep people from doing things they shouldn't do. It's a trivial task in Oracle or MySQL to write a trigger that intercepts changes to primary keys and stops them, but not at all easy in SQL Server.
What you of course would love to be able to do would be to simply do something like this:
if exists
(
select *
from inserted changed
join deleted old
where changed.rowID = old.rowID
and changed.id != old.id
)
... [roll it all back]
Which is why people go out googling for the SQL Server equivalent of ROWID. Well, SQL Server doesn't have it; so you have to come up with another approach.
A fast, but sadly not bombproof, version is to write an instead of update trigger that looks to see whether any of the inserted rows have a primary key not found in the updated table or vice versa. This would catch MOST, but not all, of the errors:
if exists
(
select *
from inserted lost
left join updated match
on match.id = lost.id
where match.id is null
union
select *
from deleted new
left join inserted match
on match.id = new.id
where match.id is null
)
-- roll it all back
But this still doesn't catch an update like...
update myTable
set id = case
when id = 1 then 2
when id = 2 then 1
else id
end
Now, I've tried making the assumption that the inserted and deleted tables are ordered in such a way that cursoring through the inserted and deleted tables simultaneously will give you properly matching rows. And this APPEARS to work. In effect you turn the trigger into the equivalent of the for-each-row triggers available in Oracle and mandatory in MySQL...but I would imagine the performance will be bad on massive updates since this is not native behavior to SQL Server. Also it depends upon an assumption that I can't actually find documented anywhere and so am reluctant to depend on. But code structured that way APPEARS to work properly on my SQL Server 2008 R2 installation. The script at the end of this post highlights both the behavior of the fast-but-not-bombproof solution and the behavior of the second, pseudo-Oracle solution.
If anybody could point me to someplace where my assumption is documented and guaranteed by Microsoft I'd be a very grateful guy...
begin try
drop table kpTest;
end try
begin catch
end catch
go
create table kpTest( id int primary key, name nvarchar(10) )
go
begin try
drop trigger kpTest_ioU;
end try
begin catch
end catch
go
create trigger kpTest_ioU on kpTest
instead of update
as
begin
if exists
(
select *
from inserted lost
left join deleted match
on match.id = lost.id
where match.id is null
union
select *
from deleted new
left join inserted match
on match.id = new.id
where match.id is null
)
raisError( 'Changed primary key', 16, 1 )
else
update kpTest
set name = i.name
from kpTest
join inserted i
on i.id = kpTest.id
;
end
go
insert into kpTest( id, name ) values( 0, 'zero' );
insert into kpTest( id, name ) values( 1, 'one' );
insert into kpTest( id, name ) values( 2, 'two' );
insert into kpTest( id, name ) values( 3, 'three' );
select * from kpTest;
/*
0 zero
1 one
2 two
3 three
*/
-- This throws an error, appropriately
update kpTest set id = 5, name = 'FIVE' where id = 1
go
select * from kpTest;
/*
0 zero
1 one
2 two
3 three
*/
-- This allows the change, inappropriately
update kpTest
set id = case
when id = 1 then 2
when id = 2 then 1
else id
end
, name = UPPER( name )
go
select * from kpTest
/*
0 ZERO
1 TWO -- WRONG WRONG WRONG
2 ONE -- WRONG WRONG WRONG
3 THREE
*/
-- Put it back
update kpTest
set id = case
when id = 1 then 2
when id = 2 then 1
else id
end
, name = LOWER( name )
go
select * from kpTest;
/*
0 zero
1 one
2 two
3 three
*/
drop trigger kpTest_ioU
go
create trigger kpTest_ioU on kpTest
instead of update
as
begin
declare newIDs cursor for select id, name from inserted;
declare oldIDs cursor for select id from deleted;
declare #thisOldID int;
declare #thisNewID int;
declare #thisNewName nvarchar(10);
declare #errorFound int;
set #errorFound = 0;
open newIDs;
open oldIDs;
fetch newIDs into #thisNewID, #thisNewName;
fetch oldIDs into #thisOldID;
while ##FETCH_STATUS = 0 and #errorFound = 0
begin
if #thisNewID != #thisOldID
begin
set #errorFound = 1;
close newIDs;
deallocate newIDs;
close oldIDs;
deallocate oldIDs;
raisError( 'Primary key changed', 16, 1 );
end
else
begin
update kpTest
set name = #thisNewName
where id = #thisNewID
;
fetch newIDs into #thisNewID, #thisNewName;
fetch oldIDs into #thisOldID;
end
end;
if #errorFound = 0
begin
close newIDs;
deallocate newIDs;
close oldIDs;
deallocate oldIDs;
end
end
go
-- Succeeds, appropriately
update kpTest
set name = UPPER( name )
go
select * from kpTest;
/*
0 ZERO
1 ONE
2 TWO
3 THREE
*/
-- Succeeds, appropriately
update kpTest
set name = LOWER( name )
go
select * from kpTest;
/*
0 zero
1 one
2 two
3 three
*/
-- Fails, appropriately
update kpTest
set id = case
when id = 1 then 2
when id = 2 then 1
else id
end
go
select * from kpTest;
/*
0 zero
1 one
2 two
3 three
*/
-- Fails, appropriately
update kpTest
set id = id + 1
go
select * from kpTest;
/*
0 zero
1 one
2 two
3 three
*/
-- Succeeds, appropriately
update kpTest
set id = id, name = UPPER( name )
go
select * from kpTest;
/*
0 ZERO
1 ONE
2 TWO
3 THREE
*/
drop table kpTest
go

Resources