SQL trigger with parsing and existence check on another table - sql

So I have a work order table with 3 fields relevant to the question being asked here
(dbo.workorder with some sample values):
location | supervisor | ownergroup
ABC-123 | JSMITH | ALPHA
XYZ-987 | JDOE | OMEGA
ABC-123 | NULL | NULL
XYZ-987 | NULL | NULL
The last two rows are to show that sometimes the supervisor/ownergroup is not filled out when inserting a workorder row which leads to the question! I have another table called "roles" with rows such as below:
role | value
ABCSupervisor | JSMITH
ABCOwnergroup | ALPHA
XYZSupervisor | JDOE
XYZOwnergroup | OMEGA
As you can see from the "roles" table, a role is made up of the first 3 letters of the location (ALWAYS) plus the word Supervisor or Ownergroup. When the 3rd and 4th rows of the "workorder" table are inserted, I'd like to develop a trigger that would attempt to fill in the values for Supervisor/Ownergroup IF there is a match in the "roles" table. If there isn't a supervisor/ownergroup for that location prefix, then it should default to a set value (let's say supervisor='super' and ownergroup='og'). Here's what I have so far, though perhaps a different approach would be better:
CREATE TRIGGER [dbo].[OwnergroupSupervisor]
ON [dbo].[workorder]
AFTER INSERT,UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE wo
SET wo.ownergroup= (THIS IS WHERE I NEED HELP)
FROM dbo.workorder AS wo INNER JOIN inserted AS i ON wo.wonum=i.wonum
WHERE wo.ownergroup IS NULL
END
GO
I'm guessing an IF EXISTS or a CASE of some sort? Probably involving something like LEFT(wo.location,3)+'Ownergroup' perhaps?
Any and all help is greatly appreciated! Thanks!
Clarification:
wonum is the primary key for the "workorder" table. I am joining the "inserted" table (which contains the rows that are being inserted or updated) to the "workorder" table so that I am only updating the new/updated rows and not the entire "workorder" table. Nothing in my question right now has anything to do with the "roles" table. See update 1 below for a better understanding of what I'm doing...
Update 1:
I've worked out one solution, but if there is nothing in the "roles" table, it just leaves the supervisor/ownergroup null instead of changing it to a default value. I can deal with this for now, but would like a better option. Here's what I have:
CREATE TRIGGER [dbo].[OwnergroupSupervisor]
ON [dbo].[workorder]
AFTER INSERT,UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE wo
SET wo.ownergroup=(SELECT value FROM roles WHERE role=LEFT(wo.location,3)+'Ownergroup')
FROM dbo.workorder AS wo INNER JOIN inserted AS i ON wo.wonum=i.wonum
WHERE wo.ownergroup IS NULL
UPDATE wo
SET wo.supervisor=(SELECT value FROM roles WHERE role=LEFT(wo.location,3)+'Supervisor')
FROM dbo.workorder AS wo INNER JOIN inserted AS i ON wo.wonum=i.wonum
WHERE wo.supervisor IS NULL
END
I have not worked out what would happen if two rows were found in the roles table (although I'm pretty sure the application restricts this so it should be OK). But as I mentioned above, this just keeps the supervisor/ownergroup NULL if no role is found in the roles table.

How you can update the workorder table based on information in roles is as below:
update wo
set ownergroup = isnull(r.value, 'og')
from workorder wo
left outer join roles r on r.role = left(wo.location, 3) + 'Ownergroup'
where wo.ownergroup is null
Then you can do a similar update for the Supervisor role.
In theory, the full answer is something like:
create trigger [dbo].[ownergroupsupervisor]
on [dbo].[workorder]
after insert,update
as
begin
set nocount on;
update wo
set wo.ownergroup= isnull(r.value, 'og')
from dbo.workorder as wo
inner join inserted as i on wo.wonum=i.wonum
left outer join roles r on r.role = left(wo.location, 3) + 'Ownergroup'
where wo.ownergroup is null
-- Repeat for Supervisor
end
go

I'm not sure what column wonum is (though I assume it's the work order #), and if it joins them, but if it does you look like you want to update your ownergroup with the value column from the roles table
CREATE TRIGGER [dbo].[OwnergroupSupervisor]
ON [dbo].[workorder]
AFTER INSERT,UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE wo
SET wo.ownergroup= r.value
FROM
dbo.workorder AS wo
INNER JOIN
inserted AS i ON wo.wonum=i.wonum
join
roles r
on left(wo.location,3) + 'Ownergroup'=r.role
WHERE
wo.ownergroup IS NULL
END
GO
If i'm misunderstanding what you want please let me know.

Related

How to use the original value in a SQL Server update trigger to change a second record

I have a SQL Server table with student, cohort, program, grad date, and status. Some students have records that are specifically tied together – if the cohort for one row changes, the cohort for another row with status 2 needs to change as well. In most cases I catch this programmatically but there are a LOT of ways to change a student’s cohort so we’re missing some.
Essentially if someone runs the first line update statement, the second also needs to run
UPDATE StudentPrograms
SET Cohort = 201610
WHERE Student = 'A1234' AND Program = 'MBA' AND Cohort = 201510
UPDATE StudentPrograms
SET Cohort = 201610
WHERE Student = 'A1234' AND Cohort = 201510 AND Status = 2
I would like to put an update trigger on the table to change both cohorts, but the problem is I can’t figure out how to capture both the starting cohort that identifies the record that needs to change and the cohort that it would need to change to. Once the update has run and the trigger fires, all I have is the updated cohort, so I don’t know which record to change.
CREATE TRIGGER [dbo].[tr_SP_Cohort]
ON [dbo].StudentPrograms
AFTER UPDATE
AS
DECLARE #student VARCHAR(10);
DECLARE #cohort INT;
SELECT #student = i.student FROM inserted i
SELECT #cohort = i.cohort FROM inserted i
SET NOCOUNT ON;
IF UPDATE(Cohort)
UPDATE dbo.StudentPrograms
SET Cohort = #cohort
WHERE Student = #student AND Status = 2 AND Cohort = ???
Unfortunately the records do have to be in this table and not a sub table for a lot of reasons. The records are occasionally also deleted / reinserted, so I run into the same problems if I tie them together by adding another field with a key - if that record gets deleted / updated / reinserted, I still need to capture the information, record it, then record the change.
Thanks for the immediate help! Here's what I ended up with, changed a bit so it works with batch updates.
ALTER TRIGGER [dbo].[tr_SP_Cohort]
ON [dbo].StudentPrograms
AFTER UPDATE
AS
SET NOCOUNT ON;
IF UPDATE(Cohort)
UPDATE a
SET Cohort = i.Cohort
FROM dbo.StudentPrograms AS a
INNER JOIN inserted AS i ON a.Student = i.Student
INNER JOIN deleted AS d ON a.Cohort = d.Cohort
AND a.Student = d.Student
WHERE a.Status = 2
When creating a trigger, you can access the old values using the deleted table
WHERE Student = #student AND Status = 2 AND Cohort = (SELECT TOP 1 Cohort from deleted)
Ideally, you should handle this in a stored procedure but as a temporary fix you can use the following code.
They way you are handling updates in your code, it will never work if more than one row was updated.
Triggers are fired once in response to a triggering action (update,delete,insert) for all the rows affected by that action, NOT for each row affected by the triggering action.
Keeping all this in mind your temporary solution should look something like...
CREATE TRIGGER [dbo].[tr_SP_Cohort]
ON [dbo].StudentPrograms
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE s
SET s.Cohort = i.Cohort
FROM deleted d
INNER JOIN dbo.StudentPrograms s ON d.Student = s.Student
AND d.Cohort = s.Cohort
INNER JOIN inserted i ON i.Student = s.Student
WHERE s.[Status] = 2;
END

update a column in a sql table with a value from another table based on a relationship

I am working on a SQL query to update a column values with an Id from a different table
Example
Organization Table
Id Name
1 AA
2 BB
Events Table
Id Name OrgId
1 AA NULL
2 AA NULL
3 BB NULL
Now, I would like to update OrgId of Events table with its respective Id from Organization table
I did try the below query but I had explicitly do it for each organization
UPDATE Event SET OrId=
(SELECT DISTINCT O.ID FROM Organization O WHERE O.Name='AA') WHERE Name='AA'
May I know a better way to do it automatically?
Use a join:
update e
set orid = o.id
from event e join
organization o
on o.name = e.tenant;
You can perform a Merge using
MERGE Event AS e
USING Organization AS o
ON (e.Name= o.name)
WHEN MATCHED THEN
UPDATE SET e.OrgId = o.id
OUTPUT $action, inserted.*;
The output clause is optional and will print out the Ids that are inserted into the Event table.
The Merge is quite powerful as it has other clauses that can be used for cases when data is only in one table and not the other. Here's a nice post which explains things clearly. https://www.simple-talk.com/sql/learn-sql-server/the-merge-statement-in-sql-server-2008/

SQL - include results you are looking for in a column and set all other values to null

I have two tables, one with orders and another with order comments. I want to join these two tables. They are joined on a column "EID" which exists in both tables. I want all orders. I also want to see all comments with only certain criteria AND all other comments should be set to null. How do I go about this?
Orders Table
Order_Number
1
2
3
4
Comments Table
Comments
Cancelled On
Ordered On
Cancelled On
Cancelled On
In this example I would like to see for my results:
Order_Number | Comments
1 | Cancelled On
2 | Null
3 | Cancelled On
4 | Cancelled On
Thanks!
This seems like a rather trivial left join.
select o.order_number, c.comments
from orders o
left join comments c
on o.eid = c.eid
and (here goes your criteria for comments)
Tested on Oracle, there might be subtle syntax differences for other DB engines.
It depends on one condition:
Are you trying to SET the other comments to null? (replace the values in the table)
or
Are you trying to DISPLAY the other comments as null? (dont display them)
If you want to change the values in the table use
UPDATE `table` SET `column` = null WHERE condition;
otherwise use:
SELECT column FROM table JOIN othertable WHERE condition;

Using trigger in one table and updating another table

CREATE TRIGGER dbo.updateTrigger
ON dbo.Education
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF NOT (UPDATE( HighestDegreeDoc) OR UPDATE (GPA) OR UPDATE (CreditHours))
RETURN
UPDATE dbo.School
set Uploaded =1
from dbo.School
JOIN inerted i ON i.Uploaded = School.Uploaded
END
GO
What is Wrong with this code. i am trying to update field in School table , field uploaded when (HighestDegreeDoc , GPA, CrediHours) update in Education Table. NOTE Education Table has more than 15 field, (Uploaded Field in School table update only when these 3 field change)
Just a guess....
JOIN inerted i ON i.Uploaded = School.Uploaded
Should be...
JOIN inserted i ON i.SchoolId = School.SchoolId
Looks like Updated is some sort of flag that you are setting. You probably want to join on an ID column instead.
It's only a typo error : it's join inserted ;)
And I don't think your join condition is good.
Can you give us the ddl ?

SQL Delete When column a and column b does not exist

Ok, so you have something like this working. You Insert into a table from a tmp table, where the Equipment Number and the Account Number are missing...
Insert INTO ClientEquipments(
SUB_ACCT_NO_SBB,
EquipmentDate,
EquipmentText,
EquipmentNumber)
Select
replace(a.SUB_ACCT_NO_SBB,'"','') as SUB_ACCT_NO_SBB,
getdate() as edate,'' as etext,
replace(a.equipmentNumber,'"','') equipmentNumber
from clientspaymenttemp a
where not exists
(select b.equipmentNumber
from clientEquipments b
where b.sub_acct_no_sbb=replace(a.SUB_ACCT_NO_SBB,'"','') and b.equipmentNumber=replace(a.equipmentNumber,'"',''))
group by SUB_ACCT_NO_SBB,equipmentNumber
But found a problem if the Equipment Number belonged to a different account number before, then my previous query will insert a new row, with the same Equipment Number but a new Account Number.
What I need it to do is simple:
If Account Number and Equipment Number exists, leave it alone no need to insert.
If Equipment Number exists, but it's assigned to a different Account Number, delete the old row. (Import file handles assignments so I am 100% sure that it needs to be assigned to new account)
Something Like this added somewhere in the previous code:
DELETE FROM ClientEquipments
WHERE (clientEquipmentId =
(SELECT clientEquipmentId
FROM ClientEquipments AS ClientEquipments_1
WHERE (equipmentNumber = '0012345CAEC6')))
If nothing exists then Insert a new row.
:::EDIT SOME MORE INFORMATION TO HELP ME OUT:::
I am reading a CSV file:
Sample Data:
Account | Name | Address | Some Extra Stuff | Equipment Number
"1234","First1,Last1","Address 1",etc etc... "ENum1234"
"1234","First1,Last1","Address 1",etc etc... "ENum5678"
"5678","First2,Last2","Address 2",etc etc... "ENum9123"
"9123","First3,Last3","Address 3",etc etc... "ENum4567"
This gets bulked imported into a temp table. (dbo.clients_temp)
Notice how account 1234 has 2 equipment numbers.
From here I insert new accounts into dbo.clients by doing a query from dbo.clients_temp to dbo.clients
Then I update dbo.clients with new information from dbo.clients_temp (ie Account 1234 might exists but now they have a new address.)
Now that my dbo.clients table is update with new clients, and new information for existing clients, I need to update my dbo.equipments table. I was originally doing what you see above, Insert Where Not Exists Account Number and Equipment Number.
Now the problem is that since equipments do change accounts, for example, Account Number 5678 might have become inactive which I don't track or care for at the database level, but the equipment Number might now belong to Account Number 1234. In this case, my original query will insert a new row into the database, since Account 1234 and Equipment Number are not returned in the SELECT.
Ok, I have lost this now :P I will try and revisit the question later on the weekend because I just confused myself O.o
I had to modify Gordon's answer above a bit, but that did the trick...
Below is the relevant line of code that deletes the inactive accounts.
DELETE FROM ClientEquipments WHERE EquipmentNumber =
(SELECT E.equipmentNumber FROM ClientEquipments As E INNER JOIN ClientsPaymentTemp AS T
on E.equipmentNumber = T.equipmentNumber and e.SUB_ACCT_NO_SBB <> T.SUB_ACCT_NO_SBB)
-- Fix Account Numbers and Equipment Numbers
update ClientPaymentTemp
set SUB_ACCT_NO_SBB = replace(SUB_ACCT_NO_SBB,'"',''),
equipmentNumber = replace(equipmentNumber,'"','')
-- Delete Existing Accounts Mapped to New Equipment
delete e
from ClientEquipments e
inner join clientspaymenttemp t
on e.EquipmentNumber = t.EquipmentNumber
and e.SUB_ACCT_NO_SBB <> t.SUB_ACCT_NO_SBB
-- Insert New Accounts
insert into ClientEquipments
(SUB_ACCT_NO_SBB,
EquipmentDate,
EquipmentText,
EquipmentNumber)
Select
SUB_ACCT_NO_SBB,
getdate() as edate,
'' as etext,
equipmentNumber
from ClientsPaymentTemp a
where not exists (select 1 from ClientEquipments where SUB_ACCT_NO_SBB = a.SUB_ACCT_NO_SBB and EquipmentNumber = a.EquipmentNumber)
I may be misunderstanding, but if all you're looking to do is delete a record where the account number isn't equal to something and the equipment number is equal to something, can't you just perform a delete with multiple where conditions?
Example:
DELETE FROM table
WHERE
equipmentNumber = someNumber AND
accountNumber <> someAccount
You could then get the number of rows affected using ##ROWCOUNT to check the number of rows affected and then insert if nothing was deleted. The example from the TechNet link above uses the following example:
USE AdventureWorks;
GO
UPDATE HumanResources.Employee
SET Title = N'Executive'
WHERE NationalIDNumber = 123456789
IF ##ROWCOUNT = 0
PRINT 'Warning: No rows were updated';
GO
I would think you could easily adapt that to do what you're looking to do.