Find the original record and change only that not the inserted one - sql

I have tried it but not successful so far. Since my knowledge in query is limited, I thought I will better post it here.
I have students table with the following structure
create table students(
id int not null primary key identity,
sname varchar(25),
status varchar(25),
renew varchar(15),
enrollment datetime,
)
I have a number of students who has an ID, studentName(sname),status('active' or 'not-active'), renew('no' for new student, yes' for renewed student) and enrollment date.
insert into students values('jay','active','no','2010-01-01')
insert into students values('Phil','active','no','2010-01-01')
insert into students values('Cru','active','no','2010-01-01')
insert into students values('slow','active','no','2010-01-01')
insert into students values('true','active','no','2010-01-01')
insert into students values('false','active','no','2010-01-01')
Now I have an INSERT Trigger which is suppose to deactive an old student when a student is renewed. So if I insert the following which has renewal set to 'yes', it should make the already existing record 'inactive'.
insert into students values('false','active','yes','2011-01-01')
I wrote this INSERT Trigger and it works but it in-actives the old and the new inserted record both. I want only the original record to be inactivated. Also not that only enrollment date and nenew fields are different, the rest are the same between original and insert records. How to fix this? Here is my trigger
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER [dbo].[tr_renew_student]
ON [dbo].students
-- WITH ENCRYPTION
FOR INSERT
-- WITH APPEND
-- NOT FOR REPLICATION
AS
-- insert sql here
if exists(select * from inserted where Renew = 'yes')
BEGIN
UPDATE students
SET status = 'Inactive'
FROM Inserted i
INNER JOIN students T2
ON i.sname = T2.sname
END
Note that this is close approximation to my problem. Thank you

Change your update to this:
UPDATE students
SET status = 'Inactive'
FROM Inserted i
INNER JOIN students T2
ON i.sname = T2.sname
AND i.id <> t2.id
This checks that the row you are updating is NOT the newly inserted row.

Have you looked at ##identity? Years back I dealt with something similar and used ##identity to get the last created identity value; basically getting the latest identity value then setting all the records matching the criteria except the one with the ID returned via ##identity.
Read about the identity value getters here:
http://blog.sqlauthority.com/2007/03/25/sql-server-identity-vs-scope_identity-vs-ident_current-retrieve-last-inserted-identity-of-record/
Added: You're right about the inserted table. If you didn't want to / can't use the inserted table, your trigger could look something like this.
SELECT ##identity // <- this gets the last identity value inserted.
UPDATE students
SET status = 'Inactive'
WHERE students.name = (SELECT name FROM students WHERE id = ##identity)
AND id <> ##identity
Note: written from memory and not tested.

Related

SQL insert trigger for insert with update

I have two tables:
[1] Donations - with amount and pet_id field
[2] Pets - with id and total donations field
I'm trying to create a trigger which will update total donations field whenever
a new row is being inserted to Donations table. I tried this one:
create trigger update_donations
on sponserships
for insert
as
update dbo.Pets
set tot_donations = (
select new_val = inserted.amount + pets.tot_donations
from inserted
where inserted.[Pet-ID] = pets.[Animal-ID]
)
But of course it changes all records, whereas i want to change only records that are changed in the donations table.
It is usually not a good practice to store this type of derived information - you could have a view that computes it on the fly rather than a trigger that keeps it up to date. Also please note that if you go that way, you also need a delete and an update trigger...
That said, you can use the following query in your insert trigger to update the relevant record in the pets table:
update p
set p.total_donations = p.total_donations + i.amount
from dbo.Pets p
inner join inserted i on i.[Pet-ID] = p.[Animal-ID]

SQL Server : trigger firing every time

For my school project I need to add a trigger to my SQL Server database. I decided a 'no double usernames' trigger on my Users table would be relevant.
The problem is, that this trigger is firing every time I execute an INSERT query. I can't figure out why this is happening every time. I even tried different ways of writing my trigger.
The trigger I have now:
CREATE TRIGGER [Trigger_NoDuplicates]
ON [dbo].[Users]
FOR INSERT
AS
BEGIN
SET NOCOUNT ON
IF(EXISTS(SELECT Username FROM Users
WHERE Username = (SELECT Username FROM inserted)))
BEGIN;
RAISERROR('This username already exists!',15, 0)
ROLLBACK
END
END
Thanks in advance!
A trigger always fires every time, do you mean "raises an error every time"?
You currently have the following (expanded to multiple lines to make it clearer)...
IF (
EXISTS (
SELECT Username
FROM users
WHERE Username = (SELECT Username FROM inserted)
)
)
The key point here is the name of the table inserted. Past tense. It's already happened.
Anything in the inserted table has already been inserted into the target table.
So, what you need to check is that the username is in the target table more than once already.
However, it is possible to insert more than one record in to a table at once. This means that Username = (SELECT Username FROM inserted) will cause its own error. (You can't compare a single value to a set of values, and inserted can contain more than one row => more than one username...)
This is how I would approach your trigger...
IF EXISTS (
SELECT
users.Username
FROM
users
INNER JOIN
inserted
ON inserted.Username = users.Username
GROUP BY
users.Username
HAVING
COUNT(*) > 1
)
This takes the (already inserted in to) users table, and picks out all the records that mach username with any record in the inserted table.
Then it GROUPs them by they username field.
Then it filters the results to only include groups with more than 1 record.
These groups (usernames), have duplicate entries and should cause your trigger to raise an error.
An alternative is a bit more similar to your approach, but many people won't recognise it, so I generally wouldn't recommend it...
IF EXISTS (
SELECT
users.Username
FROM
users
WHERE
users.Username = ANY (SELECT username FROM inserted)
GROUP BY
users.Username
HAVING
COUNT(*) > 1
)
The ANY keyword gets very rarely used, but does what it sounds like. It allows a single value to be compared to a set of values.
Finally, if your table has an IDENTITY column, you can avoid the GROUP BY by explicitly stating you don't want to compare a row to itself...
IF EXISTS (
SELECT
users.Username
FROM
users
INNER JOIN
inserted
ON inserted.Username = users.Username
AND inserted.id <> users.id
)

SQL Server Merge Upsert only doing Updates and no inserts

I want to record student movement when they swipe their cards on the door. Every time a card id and name is presented, I want to check if its a new combination which is not already in the students table. If not present then insert a new record with FirstEntry and LastEntry being the same date and cardid and name being the one that is sent in to the respective parameters. If combination already present then only update the [LastEntry] field. At the moment my query is only updating existing records, which means only the [LastEntry] field gets updated. For update this is normal. However, it doesn't insert a new record for a new entry. CardID and Name are the composite primary keys here on the table.
I don't want to use the IF Exists(Select...) method for acheiving
this.
I want to use MERGE only.
This action is to be done on the same table without using any temp tables
After performing the action I want to see the affected records in either the update or insert scenarios. Only the following fields to be shown [cardid, name, FirstEntry, LastEntry] and not the other fields in the table.
The table has other columns which I don't want to show or return.
Here is my statement which only does updates
MERGE INTO
Students AS T
USING
(SELECT cardid, name, FirstEntry, LastEntry from Students where cardid = #id and name = #name) AS S
ON
(S.cardid = T.cardid
AND
S.name = T.name)
WHEN MATCHED THEN
UPDATE SET LastEntry = #DT
WHEN NOT MATCHED THEN
INSERT (cardid, name, FirstEntry, LastEntry) VALUES(#id, #name, #DT, #DT )
OUTPUT $action, S.cardid, S.name, S.FirstEntry, S.LastEntry;
Along with updates I want it to effect inserts as well for new records
Your using clause shouldn't be querying the Students table. That's where you put the new values you want to insert or update. It should look like this:
MERGE INTO
Students AS T
USING
(SELECT #id as cardid, #name as name, #DT as FirstEntry, #DT as LastEntry) AS S
ON
(S.cardid = T.cardid
AND
S.name = T.name)
WHEN MATCHED THEN
UPDATE SET LastEntry = s.LastEntry
WHEN NOT MATCHED THEN
INSERT (cardid, name, FirstEntry, LastEntry) VALUES(s.cardid, s.name, s.FirstEntry, s.LastEntry )
OUTPUT $action, inserted.cardid, inserted.name, inserted.FirstEntry, inserted.LastEntry;
EDIT
Your output clause should probably be using inserted.<col_name> instead of S.<col_name> for the returned column names to see the new inserted or updated values. You can also use deleted.<col_name> if you want to see the value before it got updated (will be null for inserts).

sql trigger not work as expected

when one row is added to the customer table it should:
copy to the other table called new_customer table
delete the row in customer table.
In customer table has only one field that is Phone number.
This field should be copy into the new customer table and should be deleted from the customer table.
please can anyone tell me what's wrong with this trigger.....
CREATE TRIGGER update_cus
ON customer
FOR update AS
IF (COLUMNS_UPDATED() & 1) > 0
BEGIN
INSERT INTO new_customer
(Phone number
)
SELECT 'NEW',
ins.Phone number
FROM inserted ins
SELECT 'OLD',
del.Phone number
FROM deleted del
END
My guess
CREATE TRIGGER update_cus
ON customer
FOR update AS
IF EXISTS (SELECT * FROM INSERTED)
BEGIN
INSERT INTO new_customer ([Phone number])
SELECT 'NEW', ins.[Phone number]
FROM inserted ins
DELETE customer WHERE
[Phone number] IN (SELECT [Phone number] FROM deleted)
END
You want behaviour for an INSERT but have defined the trigger for an UPDATE.
Your INSERT specifies one field, but you try to insert 2 values.
You state that you want to DELETE a record from customer, but don't have a DELETE statement.
I've refrained from writing a different trigger for you because I'm not actually 100% certain what you are trying to achieve.
Could you give examples, including what the various tables should look like before and after different actions?
EDIT
CREATE TRIGGER update_cus
ON customer
FOR insert AS
BEGIN
INSERT INTO new_customer (name, id)
SELECT name, id
FROM inserted
DELETE customer WHERE
id IN (SELECT id FROM deleted)
-- This assumes id is Unique, but as the table should normally be empty, that's fine.
END

SQL Server 2008 - Help writing simple INSERT Trigger

This is with Microsoft SQL Server 2008.
I've got 2 tables, Employee and EmployeeResult and I'm trying to write a simple INSERT trigger on EmployeeResult that does this - each time an INSERT is done into EmployeeResult such as:
(Jack, 200, Sales)
(Jane, 300, Marketing)
(John, 400, Engineering)
It should look up for the Name, Department entry pairs, such as
(Jack, Sales),
(Jane, Marketing),
(John, Engineering)
within the Employee table, and if such an employee does not exist, should insert that into the Employee table.
What I have is this with unknowns on how to fix the "???"s:
CREATE TRIGGER trig_Update_Employee
ON [EmployeeResult]
FOR INSERT
AS
IF EXISTS (SELECT COUNT(*) FROM Employee WHERE ???)
BEGIN
INSERT INTO [Employee] (Name, Department) VALUES (???, ???)
END
Schema:
Employee
--------
Name, varchar(50)
Department, varchar (50)
EmployeeResult
--------------
Name, varchar(50)
Salary, int
Department, varchar (50)
You want to take advantage of the inserted logical table that is available in the context of a trigger. It matches the schema for the table that is being inserted to and includes the row(s) that will be inserted (in an update trigger you have access to the inserted and deleted logical tables which represent the the new and original data respectively.)
So to insert Employee / Department pairs that do not currently exist you might try something like the following.
CREATE TRIGGER trig_Update_Employee
ON [EmployeeResult]
FOR INSERT
AS
Begin
Insert into Employee (Name, Department)
Select Distinct i.Name, i.Department
from Inserted i
Left Join Employee e
on i.Name = e.Name and i.Department = e.Department
where e.Name is null
End
cmsjr had the right solution. I just wanted to point out a couple of things for your future trigger development. If you are using the values statement in an insert in a trigger, there is a stong possibility that you are doing the wrong thing. Triggers fire once for each batch of records inserted, deleted, or updated. So if ten records were inserted in one batch, then the trigger fires once. If you are refering to the data in the inserted or deleted and using variables and the values clause then you are only going to get the data for one of those records. This causes data integrity problems. You can fix this by using a set-based insert as cmsjr shows above or by using a cursor. Don't ever choose the cursor path. A cursor in a trigger is a problem waiting to happen as they are slow and may well lock up your table for hours. I removed a cursor from a trigger once and improved an import process from 40 minutes to 45 seconds.
You may think nobody is ever going to add multiple records, but it happens more frequently than most non-database people realize. Don't write a trigger that will not work under all the possible insert, update, delete conditions. Nobody is going to use the one record at a time method when they have to import 1,000,000 sales target records from a new customer or update all the prices by 10% or delete all the records from a vendor whose products you don't sell anymore.
check this code:
CREATE TRIGGER trig_Update_Employee ON [EmployeeResult] FOR INSERT AS Begin
Insert into Employee (Name, Department)
Select Distinct i.Name, i.Department
from Inserted i
Left Join Employee e on i.Name = e.Name and i.Department = e.Department
where e.Name is null
End