Update based on a count of email addresses - sql-server

I need to set a Lable on a contact record based on finding a duplicate email in the contact table. The duplicate label field is in the contacts_cstm field.
This SP updates all of the records, not just the one submitted.
#EMAIL1 NVARCHAR (100)
AS
BEGIN
SET NOCOUNT ON;
update CONTACTS_CSTM set DUPLICATE_CONTACT_C = 'DUPLICATE'
where (select count(EMAIL1) from CONTACTS as C
where C.EMAIL1 = #EMAIL1 ) >1
I want to this to update when the count of the contact's email is >1.

Your where clause does not constrain the table you are updating. You need to equate some column of C with some column of CONTACTS_CSTM,

Related

SQL - trigger insert only when a user made an update

I have a table, customers_accounts, that tracks some basic information about a customer on an account. When the customer's file is opened, I synchronize the information from an external system so our user gets the most updated information.
UPDATE
customers_accounts
SET
first_name = 'bob',
last_name = 'burger'
WHERE
account_number = '12345'
When a user updates the account, I do the same query, but I update a column indicating the last user to make the change
UPDATE
customers_accounts
SET
first_name = 'bob',
last_name = 'burger',
updated_by = 'H Jon Benjamin',
updated_on = GETDATE()
WHERE
account_number = '12345'
Here's the problem I'm trying to solve. I want to track changes in a history table, but only log changes when they're made by a user, not if they're from the external system. So my plan was to create a trigger that inserts a row if the user column is not blank on the insert (since the updated_by is implicitly null above in the first update)
What I tried is this:
ALTER trigger [dbo].[Accounts_Customers_LogUpdate]
ON [dbo].[Accounts_Customers]
AFTER UPDATE
AS
DECLARE #Now AS DATETIME = GETDATE()
DECLARE #User AS NVARCHAR(150)
SELECT #User = (SELECT [updated_by] FROM INSERTED)
IF (#User IS NOT NULL)
BEGIN
SET NOCOUNT ON
INSERT INTO [dbo].[Accounts_Customers-History]
SELECT *, #User, #Now
FROM inserted
END
Accounts_Customers-History is an exact copy of the table with two additional columns, change_made_by and change_made_on
It doesn't behave how i'd expect though. It inserts whatever value is in updated_by into change_made_by, regardless of the value of updated_by in the query. So I'm getting logged activity triggered by both the user and the import.
Use UPDATE() for this:
Returns a Boolean value that indicates whether an INSERT or UPDATE attempt was made on a specified column of a table or view. UPDATE() is used anywhere inside the body of a Transact-SQL INSERT or UPDATE trigger to test whether the trigger should execute certain actions.
This means that the update function will return false for the first update statement in the question, and true for the second update statement - which is exactly what you need.
Also, please note you should always specify the columns list in an insert statement,
and also always specify the columns list in a select statement. (Why?)
A revised version of your trigger might look something like this:
ALTER TRIGGER [dbo].[Accounts_Customers_LogUpdate]
ON [dbo].[Accounts_Customers]
AFTER UPDATE
AS
DECLARE #Now as DateTime = GetDate()
IF UPDATE(updated_by)
BEGIN
-- Always specify the columns list in an insert statement!
insert into [dbo].[Accounts_Customers-History] (<Columns list>)
-- Always specify the columns list in a select statement!
select <columns list>, #Now
from inserted
END
Please note that the UPDATE() function does not give you any indication if the insert or update statement that fired the trigger was successful, nor does it give you an indication if the value of the column has actually changed - it only indicates whether that column was a part of the insert or update statement that fired the trigger - as you can read in the last paragraph of the remarks section:
If a trigger applies to a column, the UPDATED value will return as true or 1, even if the column value remains unchanged. This is by-design, and the trigger should implement business logic that determines if the insert/update/delete operation is permissible or not.

How to store existing column value in new table using trigger

I have two tables
Customer
CustomerUpdate
Structure of both tables are like this
Customer table's structure
CustomerName | CustomerId
CustomerUpdate table's structure
NewCustomerName | NewCustomerId | OldCustomerName
I have few values inserted in the Customer table. Whenever I should update the data in this table I want that the existing as well as new data should be triggered into new table CustomerUpdate.
For this I created a trigger but this is only pulling the updated data, it's not pulling the existing data..
CREATE TRIGGER trgAfterUpdate
ON [dbo].Customer
FOR UPDATE
AS
SET NOCOUNT ON
declare #NewCustomerName nchar(20);
declare #NewCustomerId nchar(20);
declare #OldCustomerName nchar(20);
declare #audit_action varchar(100);
select #NewCustomerName = i.CustomerName from inserted i;
select #NewCustomerId = i.CustomerId from inserted i;
select #OldCustomerName = c.CustomerName
from Customer c
where CustomerId = #NewCustomerId;
if update(CustomerName)
set #audit_action='Updated Record -- After Update Trigger.';
if update(CustomerId)
set #audit_action='Updated Record -- After Update Trigger.';
insert into CustomerUpdate(NewCustomerName, NewCustomerId, OldCustomername)
values(#NewCustomerName, #NewCustomerId, #OldCustomerName);
PRINT 'AFTER UPDATE Trigger fired.'
GO
Please help me out
First, selecting from the table being modified when an update trigger is executing will get the new value. These are AFTER triggers (rather than INSTEAD triggers) and therefore the update has already happened by the time the trigger fires (although it can be rolled back). If you need the old value, you should select from the DELETED pseudo-table.
Second, as pointed out by #marc_s in comments, your trigger has the hidden assumption that only one row is affected by each update. This may very well be a valid assumption for your environment, if your application only ever updates one row at a time, but in the general case, every trigger should be ready to handle the case where many rows are affected by a single update. Writing your triggers to handle multiple rows is good practice.
Third, all of your sequentially executing code is pretty much unnecessary. The old value and the new value can be retrieved and inserted all at once:
CREATE TRIGGER trgAfterUpdate
ON [dbo].Customer
FOR UPDATE
AS
BEGIN
SET NOCOUNT ON
insert into CustomerUpdate(NewCustomerName, NewCustomerId, OldCustomername)
-- case 1: ID unchanged
SELECT I.CustomerName, I.CustomerID, D.CustomerName
FROM Inserted I
JOIN Deleted D on I.CustomerID=D.CustomerID
UNION ALL
-- case 2: ID changed, Name unchanged
SELECT I.CustomerName, I.CustomerID, D.CustomerName
FROM Inserted I
JOIN Deleted D on I.CustomerName=D.CustomerName
WHERE I.CustomerID<>D.CustomerID
UNION ALL
--case 3: ID changed, Name changed
SELECT I.CustomerName, I.CustomerID, D.CustomerName
FROM Inserted I
LEFT JOIN Deleted D on I.CustomerID=D.CustomerID OR I.CustomerName=D.CustomerName
WHERE D.CustomerID IS NULL;
END

TSQL Trigger unknown Column

I have a table which have 31 Columns
PrimaryField,PersNr, a1, a2, a3, a4, ...
in the a1 - a31 fields are values. The software generate automaticly each month one row.
The user can now from the software update a field or delete it (Update to NULL).
But now I need a Trigger which make an insert in a new table for each changed field. The Problem is the comparison from the before row and the changed row. How can I get the changed field when the User Update the Row?
Here is some code you can run that should work:
CREATE TRIGGER TriggerName --The name of your trigger
ON Table1 --The table where the update happens
AFTER UPDATE
AS
INERT INTO --you new tabe namegoes here
()--your new table fields go here
SELECT --only the fields you have in the parenthesis above in the same order
FROM Inerted I
The way most triggers are is that as soon as a value is inseted updated or deleted the trigger has a special vlaue in this case fr update and inserted actions the table is called Inserted and that will grab the most current value entered.
In a SQL Server trigger, you have access to Inserted and Deleted pseudo tables; those keep the data that has been modified: the first one stores the new values, the second the old ones.
CREATE TRIGGER [dbo].[MyTableUpdate
ON [dbo].[MyTable]
AFTER UPDATE
AS
IF ##ROWCOUNT = 0
RETURN
IF NOT UPDATE (a1) AND NOT UPDATE(a2) AND NOT UPDATE(a3) AND NOT UPDATE...
RETURN
SELECT YourCOlumns FROM Inserted
SELECT YourColumsn FROM Deleted
--Compare the values as you prefer
Thank you all for helping me.
In the morning when I stand up I got an Idea :)
The point was:
1. DECLARE AND Set Variables:
DECLARE #ai1 VARCHAR(30);
DECLARE #ad1 VARCHAR(30);
....
SET #ai1 = (SELECT a1 from inserted);
SET #ad1 = (SELECT a1 from deleted);
...
To Compare every #ai and #ad
IF NOT #ai1 = #ad1 OR (#ai1 IS NULL AND #ad1 IS NOT NULL) OR (#ai1 IS NOT NULL AND #ad1 IS NULL)
BEGIN
... Insert...
END
IF NOT #ai2 = #ad2...
Thats the Trick. But thank you all very much!!!

sql server after insert trigger update 3 fields of inserted row, ONLY for specific users and ONLY if fields are blank

I don't have access to the Insert Statement so if they are blank, I don't know if the blank fields are even part of the Insert statement to begin with. Office users and Field (tablet) users insert Work Order records using different applications. To keep the field users from having to populate their Crew Name, Supervisor's name and Shop Name on every record, I've put them into a lookup table keyed on the INITIATEDBY field from the Work Order record (which is auto populated by the app). Office workers may be creating Work orders for anyone but Field Crews only create work orders for their crews so when a Field crew inserts a record I want to populate the 3 fields for them. (Actually they cannot populate the 3 fields because I have hidden them on the Work Order form they use.)
Your trigger code needs to be a set based approach. In the answer you posted you assume there will only be a single row in inserted. Something like this more along the lines of what you want.
This would be the ENTIRE body of the trigger.
Update w
set Crew = tu.Crew
, SHOP = tu.Shop
, Supervisor = tu.Supervisor
from inserted i
join TableUsers tu on tu.EmpName = i.INITBY
join WorkOrder w on i.ID = w.WOID
I figures it out myself. I just had to read enough examples to put it all together. The only thing that scares me is the IF #EMPName <> ''. Is there a better way to check if a record was retrieved in the 2nd select statement?
CREATE TRIGGER trgUpdateCrewShopSuper ON ESDWO
FOR INSERT
AS
BEGIN
DECLARE #ID nvarchar(60), #InitBy nvarchar(50), #EMPName nvarchar(50), #CREW as nvarchar(50), #Shop nvarchar(100), #Super nvarchar(50);
select #ID=i.ID, #InitBy=i.INITBY from inserted i;
select #EMPName=EmpName, #CREW=CrewName, #Shop=SHOP, #Super=Supervisor
From dbo.TabletUsers
WHERE EmpName = #InitBy
IF #EMPName <> ''
BEGIN
update dbo.WorkOrder
set Crew = #Crew, SHOP = #Shop, Supervisor = #Super
WHERE WOID = #ID
END
END

Update matching column with the selected top 1 column

I have two tables Role and Role_Imp. I need to fetch Name column's value from the 1st row of the Role table. After that I need to update Name's column value in Role_Imp table for the rows which has Names same as the selected name from the Role table.
I am using the following query, which is not working as its wrong.
UPDATE Role_Imp
SET Role_Imp.Name = 'Role Test Change'
FROM Role_Imp
INNER JOIN
Role ON Role_Imp.Name = SELECT TOP 1 Name FROM Role
How should I do this?
Seems like this should do it:
UPDATE Role_Imp
SET Name = 'Role Test Change'
WHERE Name = (SELECT TOP 1 Name FROM Role)

Resources