Trigger to keep calculated data updated in DB2 - sql

I have to maintain an integer updated consistently, so I want to make a trigger (or something related) that calculates the data and keeps it updated every time.
This is:
I have a table with the attributes: Working, Balance and Must_Work. I want to keep Working and Balance updated.
Working is the SUM of values stored in other tables.
Balance is the difference between Working and Must_work.
So far I've got this
CREATE TRIGGER calculateWorkload
AFTER INSERT ON SEMESTER_WORKLOAD
FOR EACH ROW MODE DB2SQL
BEGIN
UPDATE SEMESTER_WORKLOAD SET WORKING =
(SELECT SUM(WORKING_HOURS) FROM
(SELECT DISTINCT ID_MODULE, WORKING_HOURS FROM LECTURE WHERE ID_SW = ????))
WHERE ID_SW = ????
INSERT INTO t1.ACCOUNT (SELECT t1.ACADEMIC_SEMESTER, t1.ID_LECTURER, t1.WORKLOAD, t3.WORKING_HOURS FROM SEMESTER_WORKLOAD t1
INNER JOIN LECTURER t2 ON t1.ID_LECTURER = t2.ID_LECTURER
INNER JOIN LECTURE t3 ON t1.ID_SW = t3.ID_SW WHERE
END
Problem is, how can I make it do it for every value and not only for a given value? And after that, how do I make it so it keeps the data updated?

Related

Join to grab only non-matching records SQL

I have some data which I'm trying to clean in order to run further analysis on. One of the columns in the table is called record_type, this can either be NEW or DEL. This means that initially a NEW record might be added but then a DEL record would come in later to say that particular record is now expired (NEW and DEL records would be matched on the record_id). However both the NEW and DEL record would stay in the data, it doesn't get deleted.
So what I had planned to do is to create two CTEs, one for DEL records only and one for NEW records only:
WITH deleted_rec AS(
SELECT *
FROM main_table
WHERE record_type = 'DEL'
)
, new_rec AS(
SELECT *
FROM main_table
WHERE record_type = 'NEW'
)
Then outer join on the record_id column in both the CTEs.
SELECT *
FROM new_rec
FULL OUTER JOIN deleted_rec ON deleted_rec.record_id = new_rec.record_id
The goal would have been for the output to only include records which haven't had a DEL record come in for that record_id so that way I can guarantee that all the type NEW records I have in my final table would not have had a DEL record come in for them at any point and they would therefore all be active. However, I had forgotten that FULL OUTER JOIN return everything rather than just what didn't match so is there a way to get around this to get my desired output?
I would just use a single query with exists logic:
SELECT *
FROM main_table t1
WHERE record_type = 'NEW' AND
NOT EXISTS (SELECT 1 FROM main_table t2
WHERE t2.id = t1.id AND t2.record_type = 'DEL');
In plain English, the above query says to find all records which are NEW which also do not have associated with the same id another record having DEL.

Update Trigger Can't Reference Deleted Table

I am trying to reference the deleted table as well as the inserted table when the AFTER UPDATE trigger gets fired. The following works when trying to call the inserted table.
CREATE TRIGGER FLXUpdateTrigger ON WORK_ORDER
AFTER UPDATE
FROM
(WORK_ORDER
LEFT JOIN PART ON WORK_ORDER.PART_ID = PART.ID
LEFT JOIN PART_SITE ON WORK_ORDER.PART_ID = PART_SITE.PART_ID
JOIN Inserted AS i ON i.rowid = WORK_ORDER.rowid)
But then when I try and do the same exact thing for the delete table like so.
FROM (WORK_ORDER
LEFT JOIN PART ON WORK_ORDER.PART_ID = PART.ID
LEFT JOIN PART_SITE ON WORK_ORDER.PART_ID = PART_SITE.PART_ID
JOIN deleted AS d ON d.rowid = WORK_ORDER.rowid)
It just displays the new data that was inserted for both tables. Am I joining these tables incorrectly? I am unable to just call SELECT * FROM DELETED because I do not need all of the columns from that tuple.
You defined your trigger as "AFTER UPDATE", which means by the time your trigger has processed the DELETED rows are no longer in WORK_ORDER at all!
Well, some might be, if a row is counted as UPDATED then it's in both the DELETED and INSERTED table with the same ID, so you might get some, but you won't get all.
To solve this, reference the data exclusively through DELETED because all fields are there, or, if you need new values, you could do a UNION ALL split by in DELETED and NOT IN INSERTED vs. in DELETED and IN INSERTED.
And BTW, you can select specific fields from DELETED, you don't need to select the whole row.

SQL Update using value from join table

I tried using this sql to update a new column in a table from a value in table that already exists.
update "PROMOTION" t1
set "OFFER_CHAIN_ID" = poc."OFFER_CHAIN_ID"
from "PROMOTION_OFFER_CHAIN" poc
inner join "PROMOTION" on "PROMOTION"."ID" = poc."PROMOTION_ID"
What happened is that the first value of the join got replicated in all the subsequent entries. about a both tables. The original table has unique values all the values in the updated column are the same.
Eventually I used this SQL instead.
update "PROMOTION" t1
set "OFFER_CHAIN_ID" = poc."OFFER_CHAIN_ID"
from "PROMOTION_OFFER_CHAIN" poc
where
t1."ID" = poc."PROMOTION_ID"
This update works and duplicates all the data, 1000 unique elements in the original table, 1000 unique elements in the updated table.
Is this a bug, or is this the expected result?
SQL is behaving correctly. Your original query is:
update "PROMOTION" t1
--------^
set "OFFER_CHAIN_ID" = poc."OFFER_CHAIN_ID"
from "PROMOTION_OFFER_CHAIN" poc inner join
"PROMOTION"
-----------^
on "PROMOTION"."ID" = poc."PROMOTION_ID"
Note that the table PROMOTION is mentioned twice. Not good. So, the join takes place, producing lots of rows. Then there is no correlation to the t1 version of the table.
You don't mention the database you are using. In SQL Server, you would just do:
update p
set "OFFER_CHAIN_ID" = poc."OFFER_CHAIN_ID"
from "PROMOTION_OFFER_CHAIN" poc inner join
"PROMOTION" p
on p."ID" = poc."PROMOTION_ID";
Note the alias is used after the update (or table name with if there is no alias). Now the table is mentioned only once, so the update should behave as desired.

Oracle SQL: pick the right values for an update

I have two tables. One contains a list of tickets, one contains all the comments made in each ticket. A ticket has an owner, the comment makers are tracked by their user_id.
I want to set the owners of the tickets to the users that make the first comment. The base of the script is this:
update incident inc
inner join incidentdescription incdes on
inc.inci_id = incdes.inci_id
set inc.owner_id=incdes.user_id;
However, each ticket has several comments. I can track the first comment by the incdes.creationdate. I need to have the oldest one. However, how can I make sure that at the set part, I pick the incdes.user_id from the oldest comment from each ticket?
You can try any one of this in Oracle
Normal Update
UPDATE
INCIDENT
SET
INCIDENT.OWNER_ID =
( SELECT
INCIDENTDESCRIPTION.USER_ID
FROM
INCIDENTDESCRIPTION
WHERE
INCIDENT.OWNER_ID = INCIDENTDESCRIPTION.USER_ID )
WHERE
EXISTS
(SELECT
INCIDENTDESCRIPTION.USER_ID
FROM
INCIDENTDESCRIPTION
WHERE
INCIDENT.OWNER_ID = INCIDENTDESCRIPTION.USER_ID);
Using Inline View (If it is considered updateable by Oracle)
Note: If you face a non key preserved row error add an index to resolve the same to make it update-able
UPDATE
(SELECT
INCIDENT.OWNER_ID AS OLD,
INCIDENTDESCRIPTION.USER_ID AS NEW
FROM
INCIDENT
INNER JOIN
INCIDENTDESCRIPTION
ON INCIDENT.INCI_ID = INCIDENTDESCRIPTION.INCI_ID) T
SET
T.OLD = T.NEW;
Using Merge
MERGE INTO
INCIDENT
USING
(SELECT
T1.ROWID AS RID,
T2.INCI_ID
FROM
INCIDENT T1
INNER JOIN
INCIDENTDESCRIPTION T2
ON INCIDENT.INCI_ID = INCIDENTDESCRIPTION.INCI_ID)
ON
( ROWID = RID )
WHEN MATCHED
THEN
UPDATE SET INCIDENT.INCI_ID = INCIDENTDESCRIPTION.INCI_ID;

DELETE statement issues within a trigger definition

I have created an insert/update trigger that is designed to update information in a different table based on the data being inserted. The last thing the trigger does (or is supposed to do) is remove all data from a target table with conditions that may have changed during the insert portions of the trigger.
Everything appears to be working with the trigger except the final DELETE statement. It is executing the DELETE statement, but not following any of the conditions in the where clause. It simply deletes everything in the table.
I have even tried changing the NOT IN in the where clause to IN, and it still does the same. I have isolated the DELETE statement and tested outside the trigger and it works fine (using the same variables and subqueries).
Am I missing something with the behavior of a trigger?
Here comes the code:
ALTER TRIGGER [dbo].[cust_trgr_profile_attribute]
ON [dbo].[port_module_instance_setting]
AFTER INSERT, UPDATE
AS
DECLARE #ModuleId INT=449,
#MatchGroupModSetting VARCHAR(50) = 'AttributeGroup',
#FilterGroupModSetting VARCHAR(50) = 'FilterAttributeGroup',
#MatchAttributes TABLE (attribute_id INT),
#FilterAttributes TABLE (attribute_id INT)
INSERT INTO #MatchAttributes
SELECT DISTINCT camatch.attribute_id
FROM inserted I
JOIN core_attribute camatch ON I.value = CONVERT(VARCHAR(10), camatch.attribute_group_id)
JOIN port_module_instance pmi ON I.module_instance_id = pmi.module_instance_id
AND pmi.module_id=#ModuleId
WHERE I.name like #MatchGroupModSetting+'_'
INSERT INTO #FilterAttributes
SELECT DISTINCT cafilter.attribute_id
FROM inserted I
JOIN core_attribute cafilter ON I.value = CONVERT(VARCHAR(10), cafilter.attribute_group_id)
JOIN port_module_instance pmi ON I.module_instance_id = pmi.module_instance_id
AND pmi.module_id=#ModuleId
WHERE I.name=#FilterGroupModSetting
IF ((SELECT COUNT(*) FROM #MatchAttributes) > 0 OR (SELECT COUNT(*) FROM #FilterAttributes) > 0)
BEGIN
IF (SELECT COUNT(*) FROM #MatchAttributes) > 0
BEGIN
UPDATE cpa
SET cpa.[required]=0
FROM cust_profile_attribute cpa
JOIN #MatchAttributes ma ON cpa.attribute_id = ma.attribute_id
END
IF (SELECT COUNT(*) FROM #FilterAttributes) > 0
BEGIN
UPDATE cpa
SET cpa.[required]=0
FROM cust_profile_attribute cpa
JOIN #FilterAttributes fa ON cpa.attribute_id=fa.attribute_id
END
DELETE FROM cust_profile_attribute
WHERE attribute_id NOT IN (SELECT distinct ca.attribute_id
FROM core_attribute ca
JOIN port_module_instance_setting inst ON CONVERT(VARCHAR(10),ca.attribute_group_id) = inst.value
JOIN port_module_instance modinst ON inst.module_instance_id = modinst.module_instance_id
AND modinst.module_id = #ModuleId
WHERE inst.name like #MatchGroupModSetting + '_'
OR inst.name like #FilterGroupModSetting)
END
I have found a flaw in my fundamental logic of how the trigger works. I have now refined my understanding of what is going on and able to articulate more information for others to help. Rather than try to completely transform the original post, I thought it better to post the new info here.
The basic idea is that the port_module_instance_setting table stores a string in the that represents a setting (in this specific case, the conditions are set so that the value field will always be a number). What I am trying to accomplish is that when the value field is updated in one of these specific "settings", everything in the cust_profile_attribute table that is referenced by the old value gets deleted. In this context, the value field of port_module_instance_setting is a numeric varchar value that directly references the attribute_group_id of core_attribute. Please don't comment on best practices regarding referencing tables using different data types, as I have no control over the actual table structure :)
Everything in the trigger functions properly, except the DELETE statement at the end. It isn't doing anything. Any ideas on why it isn't deleting the attributes I want it to?
I have changed the DELETE statement as follows to reference the pre-updated valuefield.
DELETE FROM cust_profile_attribute
WHERE attribute_id IN(SELECT ISNULL(attribute_id,-1) FROM deleted d
JOIN core_attribute ca ON ca.attribute_group_id= CONVERT(INT,d.value))