Logging rows that are deleted in a table sql server - sql

i have made a topic before, but i think that i have not been really clear, i am going to try to explain it in as much detail as i possibly can.
i have a table called List, this has 32000 rows, 2000 for each year(1999-2014) which has columns: SongID,Top2000Year,Position,
What has to happen:
if someone deletes for example the year 2000, all 2000 songs of the year 2000 will be deleted, theres no problem with that
Delete from List WHERE top2000Year = 2000
But, the problem i am facing is: i need this to be logged, i created a log table called LogTable with the columns: Title,Artist,Year,Position
Title will be the song title, year is the year the song came out and position is the position in the list
those are from a different table, i join them like this
select title, name[artist],song.Year[SongYear], position from List
left join Song on Song.songid = List.songid
left join Artist on Artist.artistid = Song.artistid
where top2000Year = 2013
order by position asc
this shows a query like this http://puu.sh/i8f2u/8702ee7010.png (Censored the server, it's my teachers so i figured i'd censor it)
i am trying to fill my LogTable exactly like that, if the year 2013 is deleted, those 2000 rows should be inserted into the LogTable using a trigger
Edit:
i used this https://dbalink.wordpress.com/2008/06/20/how-to-sql-server-trigger-101/ and it worked, then i tried it with my database, and it didn't work.
Code:
USE [TOP2000DATABASE]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[TRGDeleteList]
ON list
FOR DELETE
AS
INSERT INTO List (songid,top2000Year,position)
SELECT songid,top2000Year,position FROM Inserted
INSERT INTO [dbo].[LogTabel] (title,artist,Year,position)
SELECT title,artist,year,position FROM Deleted

Just create a trigger on your table for delete action:
CREATE TRIGGER TriggerName
ON List
FOR DELETE
AS
INSERT INTO LogTable (Title, Artist, Year, Position)
SELECT s.Title, a.Name, s.Year, s.Position
FROM DELETED d
LEFT JOIN Song s ON s.songid = d.songid
LEFT JOIN Artist a ON a.artistid = s.artistid
GO
You need to join on DELETED virtual table to get all deleted rows.

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]

Best approach to populate new tables in a database

I have a problem I have been working on the past several hours. It is complex (for me) and I don't expect someone to do it for me. I just need the right direction.
Problem: We had the tables (below) added to our database and I need to update them based off of data already in our DailyCosts table. The tricky part is that I need to take DailyCosts.Notes and move it to PurchaseOrder.PoNumber. Notes is where we currenlty have the PONumbers.
I started with the Insert below, testing it out on one WellID. This is Inserting records from our DailyCosts table to the new PurchaseOrder table:
Insert Into PurchaseOrder (PoNumber,WellId,JObID,ID)
Select Distinct Cast(Notes As nvarchar(20)), WellID, JOBID,
DailyCosts.DailyCostID
From DailyCosts
Where WellID = '24A-23'
It affected 1973 rows (The Notes are in Ntext)
However, I need to update the other new tables because we need to see the actual PONumbers in the application.
This next Insert is Inserting records from our DailyCost table and new PurchaseOrder table (from above) to a new table called PurchaseOrderDailyCost
Insert Into PurchaseOrderDailyCost (WellID, JobID, ReportNo, AccountCode, PurchaseOrderID,ID,DailyCostSeqNo, DailyCostID)
Select Distinct DailyCosts.WellID,DailyCosts.JobID,DailyCosts.ReportNo,DailyCosts.AccountCode,
PurchaseOrder.ID,NEWID(),0,DailyCosts.DailyCostID
From DailyCosts join
PurchaseOrder ON DailyCosts.WellID = PurchaseOrder.WellID
Where DailyCosts.WellID = '24A-23'
Unfortunately, this produces 3,892,729 records. The Notes field contains the same list of PONumbers each day. This is by design so that the people inputting the data out in the field can easily track their PO numbers. The new PONumber column that we are moving the Notes to would store just unique POnumbers. I modified the query by replacing NEWID() with DailyCostID and the Join to ON DailyCosts.DailyCostID = PurchaseOrder.ID
This affected 1973 rows the same as the first Insert.
The next Insert looks like this:
Insert Into PurchaseOrderAccount (WellID, JobID, PurchaseOrderID, ID, AccountCode)
Select PurchaseOrder.WellID, PurchaseOrder.JobID, PurchaseOrder.ID, PurchaseOrderDailyCost.DailyCostID,PurchaseOrderDailyCost.AccountCode
From PurchaseOrder Inner Join
PurchaseOrderDailyCost ON PurchaseOrder.ID = PurchaseOrderDailyCost.DailyCostID
Where PurchaseOrder.WellID = '24A-23'
The page in the application now shows the PONumbers in the correct column. Everything looks like I want it to.
Unfortunately, it slows down the application to an unacceptable level. I need to figure out how to either modify my Insert or delete duplicate records. The problem is that there are multiple foreign key constraints. I have some more information below for reference.
This shows the application after the inserts. These are all duplicate records that I am hoping to elminate
Here is some additional information I received from the vendor about the tables:
-- add a new purchase order
INSERT INTO PurchaseOrder
(WellID, JobID, ID, PONumber, Amount, Description)
VALUES ('MyWell', 'MyJob', NEWID(), 'PO444444', 500.0, 'A new Purchase Order')
-- link a purchase order with id 'A356FBF4-A19B-4466-9E5C-20C5FD0E95C3' to a DailyCost record with SeqNo 0 and AccountCode 'MyAccount'
INSERT INTO PurchaseOrderDailyCost
(WellID, JobID, ReportNo, AccountCode, DailyCostSeqNo, PurchaseOrderID, ID)
VALUES ('MyWell', 'MyJob', 4, 'MyAccount', 0, 'A356FBF4-A19B-4466-9E5C-20C5FD0E95C3', NEWID())
-- link a purchase order with id 'A356FBF4-A19B-4466-9E5C-20C5FD0E95C3' to an account code 'MyAccount'
-- (i.e. make it choosable from the DailyCost PO-column dropdown for any DailyCost record whose account code is 'MyAccount')
INSERT INTO PurchaseOrderAccount
(WellID, JobID, PurchaseOrderID, ID, AccountCode)
VALUES ('MyWell', 'MyJob', 'A356FBF4-A19B-4466-9E5C-20C5FD0E95C3', NEWID(), 'MyAccount')
-- link a purchase order with id 'A356FBF4-A19B-4466-9E5C-20C5FD0E95C3' to an AFE No. 'MyAFENo'
-- (same behavior as with the account codes above)
INSERT INTO PurchaseOrderAFE
(WellID, JobID, PurchaseOrderID, ID, AFENo)
VALUES ('MyWell', 'MyJob', 'A356FBF4-A19B-4466-9E5C-20C5FD0E95C3', NEWID(), 'MyAFENo')
So it turns out I missed some simple joining principles. The better I get the more silly mistakes I seem to make. Basically, on my very first insert, I did not include a Group By. Adding this took my INSERT from 1973 to 93. Then on my next insert, I joined DailyCosts.Notes on PurchaseOrder.PONumber since these are the only records from DailyCosts I needed. This was previously INSERT 2 on my question. From there basically, everything came together. Two steps forward an one step back. Thanks to everyone that responded to this.

Remove row from table (cascade) that have 2 foreign keys

I have next table structure :
RoomId and HouseId are nullable, one field is always null. I got next exception while attempt to delete row from Houses table.
The DELETE statement conflicted with the REFERENCE constraint
"FK_dbo.Images_dbo.Rooms_RoomId"
Why the cascade delete fails? I have used EF code first.
EDIT
Working trigger based on #Juan Carlos example:
BEGIN TRANSACTION
delete from images
where houseId in ( select id from deleted )
delete from images
where roomId in (
select rooms.Id
from rooms
where rooms.HouseId in (select id from deleted)
)
delete from Rooms
where houseId in ( select id from deleted )
delete from houses
where ID in ( select ID from deleted )
COMMIT
The problem is the cascade order. You have follow this sequence:
Delete rooms
Delete images
Delete houses
You need create a trigger to solve that. Now the problem is I dont know how create that using Code First.
create trigger house_cascade
on Houses
instead of delete
as
set nocount on
delete from rooms
where room.id in (select i.ID
from images i
inner join deleted d
on i.house_id = d.id)
delete from images
where house_id in ( select id from deleted )
delete from houses
where ID in ( select ID from deleted )
More info here
Especially this comment
But SQL Server doesn't support this. It's supper annoying, no other serious DB engine has this problem, people complained about it in 2005, Microsfot agreed that it was a "desirable feature" in 2008, but still here in 2014 they don't have it. – Shavais Aug 5 '14 at 21:28

Update a SQL table with values from another nested query

I am currently using a SQL Server Agent job to create a master user table for my in-house web applications, pulling data from 3 other databases; Sharepoint, Practice Management System and Our HR Database.
Currently it goes...
truncate table my_tools.dbo.tb_staff
go
insert into my_tools.dbo.tb_staff
(username
,firstname
,surname
,chargeoutrate)
select right(wss.nt_user_name,
,hr.firstname
,hr.surname
,pms.chargeoutrate
from sqlserver.pms.dbo.staff as pms
inner join sqlserver.wss_content.dbo.vw_staffwss as wss
on pms.nt_user_name = wss.nt_user_name
inner join sqlserver.hrdb.dbo.vw_staffdetails as hr
on wss.fullname = hr.knownas
go
The problem is that the entire table is cleared as the first step so my auto increment primary key/identified on tb_staff is certain to change. Also if someone is removed from sharepoint or the PMS they will not be recreated on this table and this will cause inconsistencies throughout the database.
I want to preserve entries in this table, even after they are removed from one of the other systems.
I suppose what I want to do is:
1) Mark all exiting entries in tb_staff as inactive (using a column called active and set it to false)
2) Run the query on the three joined tables and update every found record, also marking them as active.
I can't see how I can nest a select statement within an Update statement like I have here with the Insert statement.
How can I achieve this please?
*please note I have edited my SQL down to 4 columns and simplified it so small errors are probably due to rushed editing. The real query is far bigger.
WITH source AS(
SELECT RIGHT(wss.nt_user_name, 10) nt_user_name, /*Or whatever - this is invalid in the original SQL*/
hr.firstname,
hr.surname,
pms.chargeoutrate
FROM staff AS pms
INNER JOIN vw_staffwss AS wss
ON pms.nt_user_name = wss.nt_user_name
INNER JOIN vw_staffdetails AS hr
ON wss.fullname = hr.knownas
)
MERGE
INTO tb_staff
USING source
ON source.nt_user_name= tb_staff.username /*Or whatever you are using as the key */
WHEN MATCHED
THEN UPDATE SET active=1 /*Can synchronise other columns here if needed*/
WHEN NOT MATCHED BY TARGET
THEN INSERT (username, firstname, surname, chargeoutrate, active) VALUES (nt_user_name,firstname, surname, chargeoutrate, 1)
WHEN NOT MATCHED BY source
THEN UPDATE SET active=0;

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