SQL Server 2008 - Help writing simple INSERT Trigger - sql

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

Related

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.

Replace values in column with Oracle

How can I change all the values of a single column for other ones in one single order?
For example, I want to change old values of the last column salary (2250,1,3500,1) for new ones (2352,7512,4253,1142). I have this database:
I know how to do it but changing step by step, and it is not efficient if I have a lot of rows. This way:
UPDATE TABLE tablename
SET salary = REPLACE(tablename.salary, 2250, 2352);
and then perform that operation multiple times.
UPDATE TABLE tablename
SET salary = 2250
WHERE salary = 2352
I'm not sure what you're aiming for with the REPLACE() function but if you want to change the values then you need to do it like the above code. Set the salary to what you want WHERE it has a salary of 2250.
You can write it a few times with the different criteria and then run it.
EDIT: Since you're worried about doing this numerous times you can create a table called salaries:
CREATE TABLE t_salary AS
SELECT salary from tablename;
ALTER t_salary add newsalary integer after salary;
In the 'newsalary' column you can add what the new salary should be then do an inner join. I just created a table for this purpose called stackoverflow (which would be your 'tablename'
update stackoverflow s
inner join t_salary ns on s.salary = ns.salary
set s.salary = ns.newsalary;
Now what this will do is join tablename to t_salary where the current salary = the salary in t_salary. Then you set the tablename.salary equal to the new salary, this worked for me I hope it works for you.
Note, the syntax may be slightly different since I don't have Oracle installed on my home machine, I used MySQL.
Since you already a list old salary values and their corresponding new salary values you can place them in a flat file and create an external table in oracle to point to this file.
Once that is done then you can just fire a simple update statement similar to the one given below:
update test1 set salary = ( select newsalary from test2 where test1.empid = test2.empid);

How to perform a mass SQL insert to one table with rows from two seperate tables

I need some T-SQL help. We have an application which tracks Training Requirements assigned to each employee (such as CPR, First Aid, etc.). There are certain minimum Training Requirements which all employees must be assigned and my HR department wants me to give them the ability to assign those minimum Training Requirements to all personnel with the click of a button. So I have created a table called TrainingRequirementsForAllEmployees which has the TrainingRequirementID's of those identified minimum TrainingRequirements.
I want to insert rows into table Employee_X_TrainingRequirements for every employee in the Employees table joined with every row from TrainingRequirementsForAllEmployees.
I will add abbreviated table schema for clarity.
First table is Employees:
EmployeeNumber PK char(6)
EmployeeName varchar(50)
Second Table is TrainingRequirementsForAllEmployees:
TrainingRequirementID PK int
Third table (the one I need to Insert Into) is Employee_X_TrainingRequirements:
TrainingRequirementID PK int
EmployeeNumber PK char(6)
I don't know what the Stored Procedure should look like to achieve the results I need. Thanks for any help.
cross join operator is suitable when cartesian product of two sets of data is needed. So in the body of your stored procedure you should have something like:
insert into Employee_X_TrainingRequirements (TrainingRequirementID, EmployeeNumber)
select r.TrainingRequirementID, e.EmployeeNumber
from Employees e
cross join TrainingRequirementsForAllEmployees r
where not exists (
select 1 from Employee_X_TrainingRequirements
where TrainingRequirementID = r.TrainingRequirementID
and EmployeeNumber = e.EmployeeNumber
)

Data to be inserted by SSIS

I have a table known as Customer(DATABASE AAA) containing 3 fields
Cust_id Cust_name Cust_salary
1 A 2000
2 B 3000
3 C NULL
I want to put data of these 3 columns in Employee(DATABASE BBB) which has the same structure as of Customer.
I want to transfer records of only those customer in which Cust_salary part is not null.
This work is to be done in SSIS only. MY values for Cust_id is auto generated & before putting values to Employee_id,the Employee table should be deleted.The auto generated identity should be preserved.
You could create a SQL Execute Task in SSIS and run the following:
INSERT INTO Employee
(EmployeeId, EmployeeName, EmployeeSalary)
SELECT Cust_id, Cust_name, Cust_salary
FROM Customer
WHERE Cust_salary IS NOT NULL
Darren Davies answer seems correct, but if for some obscure reason you have an EmployeeID is also an identity column and needs to match Cust_ID, and assuming any entries already in the Employee table correspond with the correct customer you can use an Execute SQL Task in SSIS with a connection open to Database BBB to run the following:
SET IDENTITY_INSERT Employee ON
INSERT INTO Employee (EmployeeID, EmployeeName, EmployeeSalary)
SELECT Cust_ID, Cust_Name, Cust_Salary
FROM AAA..Customer
WHERE Cust_Salary IS NOT NULL
AND NOT EXISTS
( SELECT 1
FROM Employee
WHERE EmployeeID = Cust_ID
)
SET IDENTITY_INSERT Employee OFF
This will maintain the integrity of the Identity fields in each table, and only insert new Customers to the Employee table.
what have you tried?
You will need two connections, one for each DB and one data flow component which will have a OleDBSource and an OleDBDestination component inside.
On the OleDBSource you can select your connection and write your query and then you drag the green arrow to the OleDBDestination. Double click the OleDBDestination select destination connection and table and click on mapping.
Should be it

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

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.