How to create trigger on Update in transact sql, to set another field in updated row?
For example:
UPDATE table SET true_false = 1 WHERE ID = #ID
will run command:
UPDATE table SET date = GETDATE() WHERE ID = #ID
.
Please help. I can't figure it out ;)
Keep in mind that you must always allow for the possibility of multi-row updates in any trigger you write.
create trigger tr_U_YourTable
on YourTable
for Update
as
begin
if update(true_false)
update yt
set date = getdate()
from Inserted i
inner join Deleted d
on i.ID = d.ID
inner join YourTable yt
on i.ID = yt.ID
where coalesce(i.true_false,0) <> coalesce(d.true_false,0)
end
Related
Ok, I have a query that will bring 1 or more records I know how to update but I need to insert the same PN and Keyvalue to a temp table so I can revert my update at a later time.
I could create a table var and put the records in it then update from the table var and do my insert from the table var.
Is that the most efficient method?
declare #tempTable table(PART nvarchar(30), [ENTDATE] [datetime])
declare #entdate datetime
set #entdate = getdate()
insert into #tempTable(PIW_PART, PIW_ENTDATE)
select mfgp.part, #entdate from [dbo].[PARTMFGS] mfgp
inner join PARTS p on mfgp.MFP_PART = par_code
where mfgp.MFP_MANUFACTPART = 'ddd1' and mfgp.MFP_MANUFACTURER = 'udy'
and (PREVENTREORDERS = '+')
update rp
set PREVENTREORDERS = '-',
UPDATED = getdate(),
UPDATEDBY = 'IONUSER'
from PARTS rp
inner join #tempTable tt on tt.PART collate Latin1_General_BIN = code
--#tempTable
insert into PARTS_temp (PIW_PART, PIW_ENTDATE)
select * from #tempTable
Goal: Create a trigger function to update a table, payment_detail. The table payment_detail comprises of an inner join of two other tables (customer & payment). The trigger function is focused only on updating payment_detail when an UPDATE operation occurs on the payment table.
Step 1: Create detail table
CREATE TABLE IF NOT EXISTS payment_detail AS
SELECT customer.customer_id,
customer.first_name,
customer.last_name,
payment.payment_id,
payment.amount,
to_char(payment.payment_date, 'Mon YYYY') AS month_yr
FROM payment
INNER JOIN customer ON payment.customer_id = customer.customer_id;
The table above works great. Next, I create a trigger function to automatically update the above table (I know, VIEWS would be better than updating a table, but that's not the problem):
Step 2: Create trigger function
CREATE OR REPLACE FUNCTION update_payment_detail()
RETURNS TRIGGER LANGUAGE PLPGSQL AS
$$
BEGIN
UPDATE payment_detail
SET
customer_id = NEW.customer_id,
first_name = customer.first_name,
last_name = customer.last_name,
payment_id = NEW.payment_id,
amount = NEW.amount,
month_yr = to_char(NEW.payment_date, 'Mon YYYY')
FROM
customer
WHERE customer.customer_id = NEW.customer_id;
RETURN NULL;
END;
$$
CREATE TRIGGER update_payment_detail
AFTER UPDATE
ON payment
FOR EACH ROW
EXECUTE PROCEDURE update_payment_detail();
Step 3: Test the trigger function
Now, in order to test the trigger function, I update the payment table as follows:
UPDATE payment
SET amount = 4.35
WHERE payment_id = 32126 AND customer_id = 1;
View the payment_detail table to verify updated record:
SELECT * FROM payment_detail WHERE customer_id = 1;
The result is a single record (the same exact record I updated above) being repeated throughout the entire payment_detail table and that's the problem. Why does it do that? There should only be one such record, among many other unique records. And if I DROP the payment_detail at this point and then re-create it and then just run the SELECT * FROM payment_detail statement above, the payment_detail table comes out perfectly fine with just the one record updated. So it's not clear what is happening here. How could I resolve this?
Take a look at your update script:
UPDATE payment_detail
SET
customer_id = NEW.customer_id,
first_name = customer.first_name,
last_name = customer.last_name,
payment_id = NEW.payment_id,
amount = NEW.amount,
month_yr = to_char(NEW.payment_date, 'Mon YYYY')
FROM
customer
WHERE customer.customer_id = NEW.customer_id;
It says you update all payment_detail records with the data from customer of currently updated record. So on every update you update all the rows in payment_detail while you should update only the rows matching current payment (add AND payment_detail.payment_id = OLD.payment_id to your WHERE part).
EDIT 1
So the result UPDATE statement would look like:
UPDATE payment_detail SET
customer_id = NEW.customer_id,
first_name = customer.first_name,
last_name = customer.last_name,
payment_id = NEW.payment_id,
amount = NEW.amount,
month_yr = to_char(NEW.payment_date, 'Mon YYYY')
FROM
customer
WHERE
customer.customer_id = NEW.customer_id
AND payment_detail.payment_id = OLD.payment_id;
You have to use OLD.payment_id(not NEW) in WHERE clause to handle cases where payment ID changes.
Hi I created trigger insert which work fine on test field
1. when i copy data to table on which trigger is set or i use code insert into.
The insert look like this:
SET ANSI_NULLS ON GO
SET QUOTED_IDENTIFIER ON GO
Create TRIGGER sometr ON [dbo].[mpomiar] For INSERT
AS
Begin
iNSERT INTO
[Database].dbo.[eksport] ([Repair_Date],[idKard],[Position],[error][Pos_X],[Pos_Y],[Pos_Z],[name],[model],[parent])
SELECT i.[Repair_Date],c.idKard,i.[Position],i.error,i.[Pos_X],i.[Pos_Y],i.[Pos_Theta],s.name, mo.Model, pn.Parent
FROM inserted i
inner join dbo.test t on i.idtst=t.idtst
inner join dbo.Kard c on t.idKard=c.idKard
inner join dbo.TFILE AS s ON t.idTFILE = s.idTFILE INNER JOIN
dbo.MACHINE AS mc ON t.idMC = mc.idMC INNER JOIN
dbo.PANEL AS p ON c.idPANEL = p.idPANEL INNER JOIN
dbo.PARENT AS pn ON i.idPARENT = pn.idPARENT INNER JOIN
dbo.MODEL AS mo ON pn.idMOD = mo.idMOD
where [Repair_Error] in (400,300) and c.idKard not in (select idKard from dbo.eksport)
end
But when the trigger is set on real table in the MS SQL Server 2005 it only fire when i delete from code
where [Repair_Error] in (400,300) and c.idKard not in (select idKard from dbo.eksport)
We tested trigger and it works without:
where [Repair_Error] in (400,300)
but it works with:
where c.idKard not in (select idKard from dbo.eksport)
The trigger must work with where [Repair_Error] in (400,300)
I'm trying to search the same text across to fields in my database for a livesearch box.
SELECT DISTINCT u.id, u.username FROM
users AS u, user_invoice AS ui, user_roles AS ur, roles AS r WHERE
u.id = ur.user_id AND
ur.role_id = r.id AND
r.name = 'teacher' AND
(
ui.user_id = u.id AND
CAST(ui.invoice AS TEXT) = 'searchterm'
)
This query searches the invoice table and returns results properly and extremely quickly.
SELECT DISTINCT u.id, u.username FROM
users AS u, user_invoice AS ui, user_roles AS ur, roles AS r WHERE
u.id = ur.user_id AND
ur.role_id = r.id AND
r.name = 'teacher' AND
(u.username like '%searchterm%')
This query searches for a matching username and returns extremely quickly as well.
But when I combine the two like this:
SELECT DISTINCT u.id, u.username FROM
users AS u, user_invoice AS ui, user_roles AS ur, roles AS r WHERE
u.id = ur.user_id AND
ur.role_id = r.id AND
r.name = 'teacher' AND
(
u.username like '%searchterm%' OR
(
ui.user_id = u.id AND
CAST(ui.invoice AS TEXT) = 'searchterm'
)
)
It returns the proper results but take almost a minute to do so. What am I doing wrong?
EDIT: EXPLAINs of my queries:
First:
http://explain.depesz.com/s/PvS
Second:
http://explain.depesz.com/s/D5c
Combined:
http://explain.depesz.com/s/Dhf
Edited for mistake in copying the cast lines.
Here's how I solve this problem in my main app.
I have a main entity that I want users to be able to search for. Call it customer. This entity has associated detail records in a 1:n contact (for phone, email, etc) table.
I define a view, customer_quicksearch, that calculates a quicksearch key - a text field containing the concatenation of contact records for a customer along with some of the customer fields directly.
I've added triggers to customer and contact customer_summary table. The customer trigger adds a record to customer_summary when a row is inserted into customer and delete the row when the customer record is deleted. They update customer_summary by SELECTing an updated quicksearch key from `customer_quicksearch. I could use a SQL function for this instead of a view, but found the view both more useful and faster. With a view it's quicker to calculate the quicksearch keys for all customers, say, after a bulk insert or update.
CREATE VIEW customer_quicksearch AS
SELECT
customer.id AS customer_id, array_to_string(ARRAY[
customer.code,
customer.name,
string_agg(array_to_string(ARRAY[
contact.email::text,contact.altemail::text, contact.mobile_phone, contact.work_phone, contact.home_phone, contact.fax
],'|'),'|')
], '|') AS quicksearch_key
FROM customer
LEFT OUTER JOIN contact ON (customer.id = contact.customer_id)
GROUP BY customer.id;
and one of the triggers:
CREATE OR REPLACE FUNCTION customer_summary_update_for_contact() RETURNS trigger AS $$
DECLARE
_customer_id integer;
BEGIN
-- When a contact is added/removed/changed we have to regenerate the customer search key
IF tg_op = 'INSERT' OR tg_op = 'UPDATE' THEN
_customer_id = NEW.customer_id;
ELSE
_customer_id = OLD.customer_id;
END IF;
UPDATE customer_summary
SET quicksearch_key = (SELECT quicksearch_key FROM customer_quicksearch WHERE customer_id = _customer_id)
WHERE customer_id = _customer_id;
RETURN NULL;
END;
$$
LANGUAGE 'plpgsql'
SET search_path = 'public';
CREATE TRIGGER customer_summary_update_for_contact_trg AFTER INSERT OR UPDATE OR DELETE ON contact
FOR EACH ROW EXECUTE PROCEDURE customer_summary_update_for_contact();
You also need a trigger on customer to handle insert, update and delete of customer, maintaining the customer_summary record for that customer appropriately.
The customer_summary table contains records that include a quicksearch_key that's a pipe-concatenation of fields, like:
'1800MA|1800 MAKE IT BUILDERS|info#1800makeit.example.com|1234 5678|0499 999 999'
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^
[from customer record] [from 1st contact record] [from another contact record]
This is searched with a simple LIKE pattern. I could add a text_pattern_ops index on it for improved performance if I was doing prefix searches, but since I'm mostly doing searches with no left or right anchor - LIKE '%search%' - there's no benefit.
No-op (transformed into JOIN syntax) (not an anwer!) :
SELECT DISTINCT u.id, u.username
FROM
users AS u
JOIN user_invoice AS ui ON u.username like '%searchterm%'
OR ( ui.user_id = u.id AND ui.invoice = CAST('searchterm' AS INTEGER))
JOIN user_roles AS ur ON u.id = ur.user_id
JOIN roles AS r ON ur.role_id = r.id
WHERE r.name = 'teacher'
;
The CAST('searchterm' AS INTEGER)) makes no sense to me. Double quotes? parameter?
I have a trigger which is as follows:
ALTER TRIGGER [trigger_CATEGORY_VALUE_ID] ON [dbo].[tblA]
FOR UPDATE
AS
SET NOCOUNT ON
IF ( UPDATE([CATEGORY_VALUE_ID]))
BEGIN
INSERT INTO [dbo].[htblB]
( ID
, CATEGORY_VALUE_ID
, STATUS_END_DATE
, STATUS_END_DATE_SOURCE)
SELECT
t.ID
, t.CATEGORY_VALUE_ID
, GETDATE()
, t.UPDATE_SOURCE
FROM [dbo].[tblCAPITATION] t
INNER JOIN inserted ins
ON t.CATEGORY_VALUE_ID = ins.CATEGORY_VALUE_ID
END
What it needs to do is insert a new row in htblB when the column CATEGORY_VALUE_ID is updated. It works fine if only one row is updated. But if it has multiple row updates, then 2 to the power number of rows updated amount of new rows are inserted in htblB.
UPDATE dbo.tblCAPITATION
SET CAPITATION_STATUS_CATEGORY_VALUE_ID = '80574', UPDATE_SOURCE = 'TEST3'
WHERE CAPITATION_ID = 2 OR CAPITATION_ID = 3
This statement will insert 4 new rows to htblB instead of 2.
May you please shed some light on why this is hapening and how to prevent it?
Thanks!
I'm going to assume that ID is the primary key, if so then you should be joining inserted on ID not category_value_id
ALTER TRIGGER [trigger_CATEGORY_VALUE_ID] ON [dbo].[tblA]
FOR UPDATE
AS
SET NOCOUNT ON
IF ( UPDATE([CATEGORY_VALUE_ID]))
BEGIN
INSERT INTO [dbo].[htblB]
( ID
, CATEGORY_VALUE_ID
, STATUS_END_DATE
, STATUS_END_DATE_SOURCE)
SELECT
t.ID
, t.CATEGORY_VALUE_ID
, GETDATE()
, t.UPDATE_SOURCE
FROM [dbo].[tblCAPITATION] t
INNER JOIN inserted ins
ON t.ID = ins.ID
END