Updating table values in a trigger depending on data subset - sql-server-2008

OK, the title is a mouthful.
Basically, it means that when dealing with rows from the inserted table, depending on a value in a specific column, which splits the rows into one of two subsets, the data get dealt with in one of two manners. Now this could have been iterated over with a cursor, likely CTE too, but is there another way, the following (pseudo) code looks UGLY and doesn't actually work, but it gives you an idea of what I'm looking for:
Trigger
ALTER trigger [dbo].[tcdA_Combined_ActiveUnitShiftExpiredStatus_UpdateShift] on [dbo].[cd_units] after update as
begin
SET NOCOUNT ON
IF UPDATE(shift_start)
BEGIN
IF(inserted.ag_id <> 'FIRE')
BEGIN
update cd_units set shift_expired_status = 0
from inserted
where inserted.unid = cd_units.unid and inserted.shift_start <= dbo.get_dts()
END
ELSE
BEGIN
update cd_units set shift_expired_status = 0
from inserted
where inserted.unid = cd_units.unid and inserted.shift_start >= dbo.get_dts()
END
update cd_units set sask911_shift_end = (select substring(shift_start,5,2)+'/'+substring(shift_start,7,2)
+' '+substring(shift_start,9,2)+':'+substring(shift_start,11,2) from inserted)
from cd_units join inserted on cd_units.unid=inserted.unid;
END
END
As always, thanks in advance for all the help

I think the main problem here is that you are treating inserted as a single row, whereas all triggers in SQL Server are table level triggers. Thus, inserted is a table, and you can't compare a column to a single value. I would do it with something like the following.
This part is for both branches:
update cd_units set shift_expired_status = 0
from inserted
where inserted.unid = cd_units.unid
and inserted.shift_start <= dbo.get_dts();
This part only updates when inserted.ag_id = 'FIRE':
update cd_units
set sask911_shift_end = substring(inserted.shift_start,5,2) + '/' +
substring(inserted.shift_start,7,2) + ' ' +
substring(inserted.shift_start,9,2) + ':' +
substring(inserted.shift_start,11,2)
from cd_units join inserted on cd_units.unid=inserted.unid
where inserted.ag_id = 'FIRE';

Related

MySQL Trigger with Multiple IF Statements

I am new to this and cant seem to find a situation similar to mine and Im sure the answer is simple.
I have two columns, one is a predefined licenseplate ("licenseplate") and one that will be a user inputted licenseplate ("enterlicenseplate").
I made a third column which will be the sort of error check column and will have its values inputted by a trigger. Basically if the entered license plate is the same as the predefined licenseplate, set that third column to 0, if the entered license plate is null(not inputted anything yet( set that to 1, and if theres values in both and they dont match, set it 2. But i keep getting script errors. Not sure if its my syntax or im going about this all wrong.
I appreciate any help!
CREATE TRIGGER MatchPlateOnUPDATE
BEFORE UPDATE ON wp_wpdatable_3
FOR EACH ROW
BEGIN
IF (NEW.enterlicenseplate is NULL) THEN
UPDATE wp_wpdatatable_3
SET MatchingPlates = 0;
ELSEIF (NEW.enterlicensplate = New.licenseplate) THEN
UPDATE wp_Wpdatatable_3
SET MatchingPlates = 1;
ELSE UPDATE wp_Wpdatatable_3
SET MatchingPlates = 2;
END
Your problem with the if statement is that you are missing closing end if;.
But there is more: a trigger cannot action the table it was fired upon - and even if it could, then your query would basically update all rows in the table, since your updates have no where clause.
Here, you just need to modify the value of column matchingPlates in the record that is about to be inserted:
delimiter //
create trigger matchplateonupdate
before update on wp_wpdatable_3
for each row
begin
if new.enterlicenseplate is null then
set new.matchingplates = 0;
elseif new.enterlicensplate = new.licenseplate then
set new.matchingplates = 1;
else
set new.matchingplates = 2;
end if;
end;
//
delimiter ;

MySQL - Update Column Value with Function and Trigger Before Insert

i'm trying to update 2 column with trigger before insert, but i have unexpected result. i insert some data and the 2 last column will automatically inserted with values, here my first attempt
see? the last 2 column is null even i set some trigger and function to fill that columns automatically. Here my second attempt WITH EXACTLY SAME DATA
the last 2 column is filled with data, but i don't understand why the first attempt is fail?
here my trigger i use in the column total_harga
CREATE TRIGGER `set_total_harga` BEFORE INSERT ON `tbl_transaksi_detail`
FOR EACH ROW BEGIN
set NEW.total_harga = hitungTotalHargaPerItem(NEW.qty, NEW.harga_satuan);
END
trigger for column harga_satuan
CREATE TRIGGER `set_harga_satuan` BEFORE INSERT ON `tbl_transaksi_detail`
FOR EACH ROW BEGIN
set NEW.harga_satuan = set_Harga_Unit(NEW.unit, NEW.id_barang, NEW.no_transaksi);
END
function set_Harga_Unit
BEGIN
DECLARE
q,
id_toko INT;
SET
id_toko =(
SELECT DISTINCT
`tbl_transaksi`.`id_toko`
FROM
`tbl_transaksi`,
`tbl_transaksi_detail`
WHERE
`tbl_transaksi`.`no_transaksi` = no_trans
); IF unit = "PCS" THEN
SET
q =(
SELECT
`tbl_harga_jual`.`harga_pcs`
FROM
`tbl_harga_jual`
WHERE
`tbl_harga_jual`.`id_barang` = id_brg AND `tbl_harga_jual`.`id_toko` = id_toko
); RETURN q; ELSEIF unit = "PAK" THEN
SET
q =(
SELECT
`tbl_harga_jual`.`harga_pak`
FROM
`tbl_harga_jual`
WHERE
`tbl_harga_jual`.`id_barang` = id_brg AND `tbl_harga_jual`.`id_toko` = id_toko
); RETURN q; ELSEIF unit = "KARTON" THEN
SET
q =(
SELECT
`tbl_harga_jual`.`harga_karton`
FROM
`tbl_harga_jual`
WHERE
`tbl_harga_jual`.`id_barang` = id_brg AND `tbl_harga_jual`.`id_toko` = id_toko
); RETURN q;
END IF; RETURN q;
END
function hitungTotalHargaPerItem
BEGIN
DECLARE hasil int;
set hasil = qty * harga_satuan;
RETURN hasil;
END
The root cause serms to be the select that sets id_toko variable's value:
SET
id_toko =(
SELECT DISTINCT
`tbl_transaksi`.`id_toko`
FROM
`tbl_transaksi`,
`tbl_transaksi_detail`
WHERE
`tbl_transaksi`.`no_transaksi` = no_trans
);
In the select you inner join tbl_transaksi_detail (the table with the trigger in question) on another table. However, in the 1st case tbl_transaksi_detail is still empty (the trigger is before insert), therefore id_toko variable is set to null.
This will result q being null, which in turn results in the entire calculation set to null.
In the 2nd case there is already a record inserted into tbl_transaksi_detail table, therefore the calculation returns a non null value. But it returns the correct values only because the 1st and 2nd records' details are exactly the same.
I do not really understand that select that calculates id_toko anyway. If that is a transaction id, then you may use last_insert_id() if it is auto increment and the transaction record has just been created or max(id_toko) to get the highest value of id_toko (this is not multi user safe).
it seem i have mistaken select query in function set_Harga_Unit, based on clues from #Shadow
SET
id_toko =(
SELECT DISTINCT
`tbl_transaksi`.`id_toko`
FROM
`tbl_transaksi`,
`tbl_transaksi_detail`<<== I DON'T NEED THIS
WHERE
`tbl_transaksi`.`no_transaksi` = no_trans
); IF unit = "PCS" THEN
when first insert in tbl_transaksi_detail, the value no_transaksi is null because i use trigger before insert in empty table (tbl_transaksi_detail), so i remove tbl_transaksi_detail from query
SET
id_toko =(
SELECT DISTINCT
`tbl_transaksi`.`id_toko`
FROM
`tbl_transaksi`
WHERE
`tbl_transaksi`.`no_transaksi` = no_trans
); IF unit = "PCS" THEN
now it working, thanks everybody!

How to check difference in each on OLD.* to NEW.* column in a MySQL Trigger?

Since SQL does not have a FOR-EACH statement, how could we verify if there is a difference on each value from the OLD object to the NEW object in a AFTER UPDATE type TRIGGER without knowing the table columns [and table names]?
Example today:
CREATE TRIGGER `audit_events_ugly`
AFTER UPDATE ON `accounts`
FOR EACH ROW
BEGIN
DECLARE changes VARCHAR(8000);
IF OLD.user_name <> NEW.user_name THEN
SET changes = 'user_name from % to %';
END IF;
IF OLD.user_type <> NEW.user_type THEN
SET changes = CONCAT(changes, ', user_type from % to %');
END IF;
IF OLD.user_email <> NEW.user_email THEN
SET changes = CONCAT(changes, ', user_email from % to %');
END IF;
CALL reg_event(how_canI_get_tableName?, #user_id, changes);
-- and that can go on and on... differently for every table.
END;
Example as I wish it could be:
CREATE TRIGGER `audit_events_nice`
AFTER UPDATE ON `accounts`
FOR EACH ROW
BEGIN
DECLARE changes VARCHAR(8000);
DECLARE N INT DEFAULT 1;
FOREACH OLD, NEW as OldValue, NewValue
BEGIN
IF OldValue <> NewValue THEN
SET changes = CONCAT(changes, ', column N: % to %');
SET N = N + 1;
END IF;
CALL reg_event(how_canI_get_tableName?, #user_id, changes);
-- now I can paste this code in every table that is audited..
END;
Any Ideas? WHILE, FOREACH, ARRAYS...
I think you cannot do that directly in a for-loop at the trigger level.
However, you could use a script to generate the trigger code. You would need to re-generate it every time you add/remove a field to the table (usually not frequently).

MySQL IF statement syntax error

I have this code:
IF ((SELECT COUNT(*) FROM polje WHERE xkoord = 0 AND ykoord = 0) > 0) THEN
UPDATE polje
SET tezina = tezina + 1
WHERE xkoord = 0 AND ykoord = 0;
it's suppose to check if a field ('polje') with the coordinates (0,0) exists, and if it does it updates it's weight ('tezina') value by 1. This code is part of a procedure but when I select it out and run just that code I get a syntax error (just like in the procedure).
What am I doing wrong?
EDIT:
Here's the whole part of the procedure:
IF ((SELECT COUNT(*) FROM polje WHERE xkoord = x AND ykoord = y) > 0) THEN
UPDATE polje
SET tezina = tezina + 1
WHERE xkoord = x AND ykoord = y;
ELSE
BEGIN
IF #koncept = 'zid' THEN SET tezina = 9999;
ELSE SET tezina = 1;
END IF;
INSERT INTO polje (xkoord, ykoord, tezina, d_tezina) VALUES (x, y, tezina, tezina);
END;
END IF;
Where there IS a field already in the database with those coordinates I get a NULL value for 'tezina' instead of tezina = tezina + 1
I'm not entirely sure why you think you even need the if statement in that first section of code - update is perfectly capable of selecting the records for you:
UPDATE polje
SET tezina = tezina + 1
WHERE xkoord = 0 AND ykoord = 0;
That will only hit rows where both coordinates are zero. If there are none, then no rows will be modified.
However, if your intent is to detect whether a row exists so you can either insert or update it (as your edit seems to suggest), the normal way of doing that is with insert ... on duplicate key ....
In your particular case, that would be something along the following lines, assuming the primary key was a composite over the coordinate columns:
INSERT INTO polje (
xkoord, ykoord, tezina, d_tezina
) VALUES (
x, y, tezina, tezina
) ON DUPLICATE KEY
UPDATE tezina = tezina + 1
Another possibility, if you don't have your primary key set up that way, is to do the update and detect if you hit any rows. If so, you're done, otherwise you need to insert a row.
The update in that case would be identical to the first one shown above. Then you simply check row_count() to see if the rows updated was zero. If it was, you then insert the first new row for this coordinate pair.
Keep in mind that the definition of "rows updated" depends on how you connect. If you connect with the option CLIENT_FOUND_ROWS, the count is the number of rows touched regardless of the current row value (count is how many rows matched the where clause).
Without that, you only get a count of the rows that were actually changed to something new.
By that, I mean, consider you have two rows:
id value
-- -----
1 A
2 B
and you run update tbl set value = 'A'
The first time you run it without CLIENT_FOUND_ROWS, row count will be set to one since only id 2 is updated. If you run it again, you'll get a row count of zero since no rows are updated.
With CLIENT_FOUND_ROWS, you'll get a row count of two no matter how many times you run it, since it's giving you the rows matched by the where clause.
Also note that the update/detect/insert should be done within a transaction to ensure no race conditions.
Just use the update clause only:
UPDATE polje
SET tezina = tezina + 1
WHERE xkoord = 0 AND ykoord = 0
If no record is found by xkoord = 0 AND ykoord = 0, then nothing will happen.

Finding min and max value of the table in a constant time

I have a table which contains relative large data,
so that it takes too long for the statements below:
SELECT MIN(column) FROM table WHERE ...
SELECT MAX(column) FROM table WHERE ...
I tried index the column, but the performance still does not suffice my need.
I also thought of caching min and max value in another table by using trigger or event.
But my MySQL version is 5.0.51a which requires SUPER privilege for trigger and does not support event.
It is IMPOSSIBLE for me to have SUPER privilege or to upgrade MySQL.
(If possible, then no need to ask!)
How to solve this problem just inside MySQL?
That is, without the help of OS.
If your column is indexed, you should find min(column) near instantly, because that is the first value MySQL will find.
Same goes for max(column) on an indexed column.
If you cannot add an index for some reason the following triggers will cache the MIN and MAX value in a separate table.
Note that TRUE = 1 and FALSE = 0.
DELIMITER $$
CREATE TRIGGER ai_table1_each AFTER INSERT ON table1 FOR EACH ROW
BEGIN
UPDATE db_info i
SET i.minimum = LEAST(i.minimum, NEW.col)
,i.maximum = GREATEST(i.maximum, NEW.col)
,i.min_count = (i.min_count * (new.col < i.minumum))
+ (i.minimum = new.col) + (i.minimum < new.col)
,i.max_count = (i.max_count * (new.col > i.maximum))
+ (i.maximum = new.col) + (new.col > i.maximum)
WHERE i.tablename = 'table1';
END $$
CREATE TRIGGER ad_table1_each AFTER DELETE ON table1 FOR EACH ROW
BEGIN
DECLARE new_min_count INTEGER;
DECLARE new_max_count INTEGER;
UPDATE db_info i
SET i.min_count = i.min_count - (i.minimum = old.col)
,i.max_count = i.max_count - (i.maximum = old.col)
WHERE i.tablename = 'table1';
SELECT i.min_count INTO new_min_count, i.max_count INTO new_max_count
FROM db_info i
WHERE i.tablename = 'table1';
IF new_max_count = 0 THEN
UPDATE db_info i
CROSS JOIN (SELECT MAX(col) as new_max FROM table1) m
SET i.max_count = 1
,i.maximum = m.new_max;
END IF;
IF new_min_count = 0 THEN
UPDATE db_info i
CROSS JOIN (SELECT MIN(col) as new_min FROM table1) m
SET i.min_count = 1
,i.minimum = m.new_min;
END IF;
END $$
DELIMITER ;
The after update trigger will be some mix of the insert and delete triggers.