SQL Server trigger to change NULL to empty strings - sql-server

I'm migrating an Access database to a SQL Server 2014 Express back end and the application as it is is designed to put empty strings in some columns in some of the tables instead of NULL's (Access's behavior on attached forms).
It turns out I can't have the table not allow nulls in these columns because when attached to a bound form via ODBC attached tables Access explicitly is trying to insert NULL values when someone simply deletes the contents of a column, even if I have a default value defined on the table.
I want to say up front that I will be fixing this to handle NULLs properly, but for 'right now', my priority is to just get the back end converted to SQL operating the same as it was in Access, so I just want a trigger to change NULL values on a few fields to empty strings until such a time when I can look at all the application logic which is expecting empty strings in these fields right now and change it to handle NULLs.
I came up with the following:
CREATE TRIGGER TABLE1_ReplaceNulls
ON TABLE1
AFTER INSERT, UPDATE
AS
IF UPDATE(FIELDWNULL1)
BEGIN
UPDATE TABLE1
SET FIELDWNULL1 = ''
FROM inserted I
INNER JOIN TABLE1 ON I.PKFIELD = TABLE1.PKFIELD
WHERE I.FIELDWNULL1 IS NULL;
END;
This works fine for a single column. How best do I do this for multiple columns in the same table? I have one table with 4 columns that all could contain NULLS, but I want empty strings in place of them.
Should I do a separate IF block for each column that could contain the NULL or just handle it all at once? Of course, if I handle it all at once I would have to consider some of the columns might have legit values, but if I do separate statements then it could essentially run 4 updates after a column is inserted. Maybe it doesn't matter as this is all temporary, but just curious on other more experienced thoughts.

Using below update statement,update all four column in one trans. This code is not tested.
UPDATE TABLE1
SET FIELDWNULL1=iif(FIELDWNULL1 is null,'',FIELDWNULL1),
FIELDWNULL2=iif(FIELDWNULL2 is null,'',FIELDWNULL2),
FIELDWNULL3=iif(FIELDWNULL3 is null,'',FIELDWNULL3),
FIELDWNULL4=iif(FIELDWNULL4 is null,'',FIELDWNULL4)
FROM inserted I INNER JOIN TABLE1
ON I.PKFIELD = TABLE1.PKFIELD
New trigger code: With IIF statement
CREATE TRIGGER TABLE1_ReplaceNulls ON TABLE1
AFTER INSERT, UPDATE
AS
--IF UPDATE(FIELDWNULL1)
BEGIN
UPDATE TABLE1
SET FIELDWNULL1=iif(FIELDWNULL1 is null,'',FIELDWNULL1),
FIELDWNULL2=iif(FIELDWNULL2 is null,'',FIELDWNULL2),
FIELDWNULL3=iif(FIELDWNULL3 is null,'',FIELDWNULL3),
FIELDWNULL4=iif(FIELDWNULL4 is null,'',FIELDWNULL4)
FROM inserted I INNER JOIN TABLE1
ON I.PKFIELD = TABLE1.PKFIELD
--WHERE I.FIELDWNULL1 IS NULL;
END;
With ISNULL() function
CREATE TRIGGER TABLE1_ReplaceNulls ON TABLE1
AFTER INSERT, UPDATE
AS
--IF UPDATE(FIELDWNULL1)
BEGIN
UPDATE TABLE1
SET FIELDWNULL1=ISNULL(FIELDWNULL1,''),
FIELDWNULL2=ISNULL(FIELDWNULL2,''),
FIELDWNULL3=ISNULL(FIELDWNULL3,''),
FIELDWNULL4=ISNULL(FIELDWNULL4,'')
FROM inserted I INNER JOIN TABLE1
ON I.PKFIELD = TABLE1.PKFIELD
--WHERE I.FIELDWNULL1 IS NULL;
END;

Related

inserted table role in instead of insert trigger

I have a view based upon 2 tables, I have written an instead of insert trigger to insert into that view:
Create trigger tr_vWEmployeeDetails_InsteadOfInsert
on vWEmployeeDetails
Instead Of Insert
as
Begin
Declare #DeptId int
Select #DeptId = DeptId
from tblDepartment
join inserted on inserted.DeptName = tblDepartment.DeptName
if(#DeptId is null)
Begin
Raiserror('Invalid Department Name. Statement terminated', 16, 1)
return
End
Insert into tblEmployee(Id, Name, Gender, DepartmentId)
Select Id, Name, Gender, #DeptId
from inserted
End
The above code works fine, but I want to know that the insert statement to the view doesn't work, instead the trigger works, then from where do we get values in the 'inserted' magic table.
Please explain.
From the relevant MSDN page:
DML trigger statements use two special tables: the deleted table and the inserted tables. SQL Server automatically creates and manages these tables. You can use these temporary, memory-resident tables to test the effects of certain data modifications and to set conditions for DML trigger actions. You cannot directly modify the data in the tables or perform data definition language (DDL) operations on the tables, such as CREATE INDEX.
The insert on the view is blocked by the use of the instead of trigger - That's literally the meaning of "instead of".
Please note that your trigger code will fail if an insert statement to the view will try to insert multiple records. The reason for this is that in SQL Server, triggers are raised per statement, and not per row. The following code that is specified in the beginning of the trigger
Declare #DeptId int
Select #DeptId = DeptId
from tblDepartment
join inserted
on inserted.DeptName = tblDepartment.DeptName
Will fail once the inserted table will have multiple rows that matches the on clause if the join.
Triggers in SQL Server must always be written to handle multiple rows in the inserted / deleted table.

Unique entries over to tables in database

I have a problem where I need to check that two columns in each table in a database are unique.
We have the database with barcode entries called uid and rid.
Table 1: T1.uid
And
Table 2: T2.rid
No barcodes in the two table columns must be the same.
How can we ensure that.
If a insertion of a barcode into table T1.uid matches an entry in
T2.rid we want to throw an error.
The tables are cleaned up and is in a consistent state where the entries in
T1.uid and T2.rid are unique over both table columns.
It is not possible to insert NULL values in the tables respective uid and tid column(T1.uid and T2.rid)
It is not possible to create a new table for all barcodes.
Because we don't have full control of the database server.
EDIT 19-02-2015
This solution cannot work for us, because we cannot make a new table
to keep track of the unique names(see table illustration).
We want to have a constraint over two columns in different tables without changing the schema.
Per the illustration we want to make it impossible for john to exist in
T2 because he already exists in table T1. So an error must be "thrown"
when we try to insert John in T2.Name.
The reason is that we have different suppliers that inserts into these tables
in different ways, if we change the schema layout, all suppliers would
need to change their database queries. The total work is just to much,
if we force every suppplier to make changes.
So we need something unobtrusive, that doesnt require the suppliers to change
their code.
A example could be that T1.Name is unique and do not accept NULL values.
If we try insert an existing name, like "Alan", then an exception will occur
because the column has unique values.
But we want to check for uniqueness in T2.Name at the same time.
The new inserted value should be unique over the two tables.
Maybe something like this:
SELECT uid FROM Table1
Where Exists (
SELECT rid FROM Table2
WHERE Table1.uid = rid )
This will show all rows from Table1 where their column uid has an equivalent in column rid of Table2.
The condition before the insertion happens could look like below. #Idis the id you need to insert the data for.
DECLARE #allowed INT;
SELECT #allowed = COUNT(*)
FROM
(
SELECT T1.uid FROM T1 WHERE T1.uid = #Id
UNION ALL
SELECT T2.rid FROM T2 WHERE T2.rid = #id
)
WHERE
#id IS NOT NULL;
IF #allowed = 0
BEGIN
---- insert allowed
SELECT 0;
END
Thanks to all who answered.
I have solved the problem. A trigger is added to the database
everytime an insert or update procedure is executed, we catch it
check that the value(s) to be inserted doens't exist in the columns of the two
tables. if that check is succesfull we exceute the original query.
Otherwise we rollback the query.
http://www.codeproject.com/Articles/25600/Triggers-SQL-Server
Instead Of Triggers

What is the best way to figure out if an UPDATE, DELETE or INSERT occur for a trigger

I want to create a single trigger in MS SQL for INSERT, UPDATE and DELETE but I want to be able to simple extract the data without 3 IF statements
I want to do something like this:
DECLARE #PK int
SELECT #PK = COALESCE(i.PK, d.PK)
FROM inserted i, deleted d
This did not work but would like to know if I can do it in one query.
What are some alternatives?
You can do the switch logic found here: SQL Server Trigger switching Insert,Delete,Update
Or you can create 3 different triggers.
Those are you options.
I ended up using a FULL OUTER JOIN
DECLARE #DataIWanted as varchar(255)
SELECT #DataIWanted = COALESCE(i.TheData,d.TheData)
FROM inserted i
FULL OUTER JOIN deleted d on d.PK=i.PK
The query above will be from the deleted table or the inserted/updated table. With the assumption that TheData is defined as NON NULL in the DB.

Trigger not working properly on batch update

I have a trigger that check data before insert to another table
IF NOT EXISTS (SELECT *
FROM inserted,
Clients
WHERE Inserted.Account = Clients.Account)
BEGIN
INSERT INTO Clients(GUID, Account, ....)
SELECT GUID, Account
FROM inserted
END
it's working fine if one row updated
but if use batch update its not working.
for example Update Table1 set Number = Number where account <> ''
If the second table (Clients) is not empty, nothing added
if it's empty its work well
Rather than doing a check then insert, why not write it as a single INSERT statement:
Insert into Clients (GUID , Account .....)
Select i.GUID , i.Account
from
inserted i
left join
Clients c
on
i.Account = c.Account
where
c.Account is null
The NULL check in the WHERE clause can be for any column in Clients which isn't nullable.
Even if you keep the EXISTS check in, you still need something like the above, because the EXISTS check is asserting something about all of the rows in inserted - when it may consist of some rows for which the assertion is true, and some rows for which it is false.

Trigger that updates just the inserted row

I'm trying to create a simple trigger using TSQL (or SQL Server 2008). The problem is: my current trigger is updating the entire table. This was fine for a while, but now the table has more than 20k rows. So I want a trigger that only updates the rows that are being inserted.
Here's my current simple trigger:
CREATE TRIGGER trig_MyPplUpdate
ON [Persons]
FOR INSERT
AS
Begin
Update Persons
set MyFile = NULL
where Len(MyFile) < 60
End
I think I'll have to use either the "inserted" table or the row_number function ordered by the primary key. Any ideas?
If it is necessary to use a trigger here at all I would use an INSTEAD OF trigger to adjust the values pre-insert and avoid the need to JOIN back onto the base table and Update them afterwards.
CREATE TRIGGER trig_MyPplUpdate
ON [Persons]
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO Persons
SELECT foo,
bar,
CASE
WHEN Len(MyFile) >= 60 THEN MyFile
END
FROM Inserted
END
You need to join the Inserted pseudo table in your UPDATE statement. Always be aware that SQL Server fires the trigger once per statement and that statement can very well modify/insert multiple rows at once - so your Inserted table quite likely will contain more than one row - just need to keep that in mind when you write your trigger.
Try something like this:
CREATE TRIGGER trig_MyPplUpdate
ON [Persons]
FOR INSERT
AS
UPDATE dbo.Persons
SET MyFile = NULL
WHERE Len(MyFile) < 60
AND PersonID IN (SELECT DISTINCT PersonID FROM Inserted)
or use whatever unique column (your primary key) you have to get exactly those rows that have been inserted.

Resources