mysql trigger does not work if value is null - mysql

I want to track all changes of a specific column and store them to another table. This works if processed_at is updated to a value except when it is null. However I would expect that this condition
if NEW.processed_at != OLD.processed_at then
is also true if the old value is not null and the new is null, but it does not insert a new row to the processed_changes table in that case.
create definer = xxx#`%` trigger processed_change_on_update
after update
on results
for each row
begin
if NEW.processed_at != OLD.processed_at then
insert into processed_changes (result_id, operation, old_processed_at, new_processed_at)
values (NEW.id, 'update', OLD.processed_at, NEW.processed_at);
end if;
end;

using a null safe comparison saved the problem.
From Akinas comment:
When operand is NULL then the result is NULL too. NULL is treated as
FALSE. You must use NULL-safe compare. if NOT NEW.processed_at <=>
OLD.processed_at then

Related

In mysql triggers is there a way to know which column is getting update?

Does mysql have a statement that helps me to know which column is being modified in a trigger?
I already tried this way:
DELIMITER //
CREATE TRIGGER trigger
AFTER UPDATE ON myTable
FOR EACH ROW
IF OLD.ID != NEW.ID THEN
INSERT INTO backupTable
(ID,back_ID) values (NEW.ID,OLD.ID);
END IF
//
DELIMITER;
But my teacher said that is another way more effective than compare OLD.ID with NEW.ID
From inside a row level trigger in MySQL, it's not possible to directly determine which columns are referenced in the statement. MySQL doesn't give us access to the statement that cause the trigger to be fired.
Within an UPDATE trigger, we can access the "NEW" and "OLD" values of columns, and we can do comparison.
Given a statement such as this:
UPDATE myTable SET fo = IFNULL(fo,0) + 1 WHERE id = 42 ;
The id value wouldn't be changed, but if a matching row (or rows) exist, the value of the fo column in those rows will certainly be changed.
To detect that in a trigger, we need to compare OLD.fo to NEW.fo. We should also take into account a change from NULL to non-NULL or vice versa.
To detect changes to any number of columns, we would need to check each column.
IF ( OLD.id <=> NEW.id )
AND ( OLD.fee <=> NEW.fee )
AND ( OLD.fi <=> NEW.fi )
AND ( OLD.fo <=> NEW.fo )
AND ( OLD.fum <=> NEW.fum ) THEN
-- the values of these columns (id, fee, fi, fo, fum) is not changed
ELSE
-- the value of at least one of the columns id, fee, fi, fo, or fum is changed
END IF
The null-safe comparison <=> (spaceship) operator is MySQL shorthand
a <=> b
is equivalent to:
( ( a IS NOT NULL AND b IS NOT NULL AND a = b ) OR ( a IS NULL AND b IS NULL ) )
The normative pattern is for id column to be a surrogate primary key, with most or all of the desirable properties: non-null-able, simple datatype, unique, anonymous, immutable, ...
It would be very odd use-case to have the surrogate id primary key value of a row changed.

MySQL - Trigger UPDATE (How values change)

I had a Trigger "After Update" in my table, but I need know how column changes.
I dont want do this:
IF OLD.*column* <> NEW.*column* THEN ...
Someone have a trick?
No tricks. If you want to detect that the value of a column changed, you need to explicitly compare the values of OLD.col and NEW.col.
One quirk with SQL is the tri-valued boolean logic. And an inequality test to a NULL value will never return TRUE. (An expression evaluated in a boolean context can return one of three values: TRUE, FALSE or NULL.
foo <> NULL --> NULL
NULL <> NULL --> NULL
foo <> bar --> TRUE
If you want to detect whether the value of a column was changed, including a change to or from a NULL value, the inequality test won't cut it.
One trick is to use the null-safe comparison operator (the spaceship symbol <=>) which will return only TRUE or FALSE, even when one or both of the values being compared are NULL, to detect a difference:
IF NOT (NEW.col <=> OLD.col) THEN
-- value of col was modified
END IF;
Another trick is to use a SQL statement to help generate some of the code you would need in the trigger body. Referencing the columns table in the information_schema database. This is a short example, but this could be extended to include additional statements, and the END IF. The return from this can be pasted into a text editor, to help you build the trigger body.
SELECT CONCAT(' IF NOT (NEW.`',c.column_name,'` <=> OLD.`',c.column_name,'`) THEN') AS i
FROM information_schema.columns c
WHERE c.table_name = 'mytable'
AND c.table_schema = 'mydatabase'
ORDER BY c.ordinal_position
returns something like
--------------------------------------------------------------
IF NOT (NEW.`id` <=> OLD.`id`) THEN
IF NOT (NEW.`created` <=> OLD.`created`) THEN
IF NOT (NEW.`display_name` <=> OLD.`display_name`) THEN
This can be very helpful if you have a lot of columns.
Aside from that, there aren't really any other "tricks".

Dynamic value selection on INSERT statement

So my problem is the following. I've got a timeStatus column that will have one of two values on an INSERT statement, 'pending' or 'never', depending on whether the column fromDate is NULL or not.
I've made this prepared statement that doesn't work but represents what I intend. On the other hand I'm not sure if a constraint would be in order here, rather then having it specified on the statement. This way I could specify the status value for an insert or update and the table would know what to do. However I need some guidance as to what method to go with and where to go to learn it.
Here's the statement:
INSERT INTO Bservices (
servStatus, timeStatus,
fromDetails, fromDate, fromTime)
VALUES(
'pending', IF(ISNULL(`fromDate`)) 'pending' ELSE 'never',
'a', '', '')
The intended behavior is the following:
ON INSERT
if(fromDate == '') {
timeStatus = 'pending'
} else {
timeStatus = 'never'
}
ON UPDATE
if(timeStatus == 'pending' && fromDate != '') {
timeStatus = 'updated'
}
This doesn't work when you do it with expressions in the VALUES clause of an INSERT statement, because the expressions are evaluated before the row has been created. Therefore all columns are naturally NULL.
To do what you want, you need to write triggers BEFORE INSERT and BEFORE UPDATE. Something like the following, though I have not tested this so I'll leave debugging up to you:
CREATE TRIGGER insBservices
BEFORE INSERT ON Bservices
FOR EACH ROW
SET NEW.timeStatus = IF(NEW.fromDate IS NULL, 'pending', 'never');
CREATE TRIGGER updBservices
BEFORE UPDATE ON Bservices
FOR EACH ROW
SET NEW.timeStatus = IF(NEW.fromDate IS NOT NULL AND OLD.timeStatus = 'pending',
'updated', NEW.timeStatus);
Re your comment:
If you want to learn more about triggers, the MySQL manual is actually pretty weak in this area. They show you the syntax for reference, but not many examples. There are a lot of tricky parts.
For example, understanding when to use DELIMITER when you define triggers, to account for the ambiguity between semicolons inside the body of a trigger, versus the terminator of the CREATE TRIGGER statement itself. This applies to CREATE PROCEDURE and CREATE FUNCTION as well.
I wrote an example and an explanation in my answer to Create function through MySQLdb.
There are tutorials on triggers, for example:
http://code.tutsplus.com/articles/introduction-to-mysql-triggers--net-12226
http://www.mysqltutorial.org/mysql-triggers.aspx

Trigger update and handle null values

I Have a trigger that takes the old values from the orginal tabel and put these values in one cell on another table except the date and user, when any updates occurs, the orginal table has attributes which is null when the raw is inserted like (updatedate and updateuser), so when this raw is updated those two attributes will be update to the current date and user who did the update, so the trigger should get those two parameters when they are null should get the inserted values and when they are not null should get the new values,however the trigger's syntax is correct but the result is incorrect
ALTER Trigger [dbo].[OrdersUpdate] on [dbo].[Orders]
After Update
as
Declare #UpdateDate datetime, #UpdateUser uniqueidentifier
Set #UpdateDate=(Select UpdateDate from deleted)
Set #UpdateUser=(Select UpdateUser from deleted)
If #UpdateDate is null
Begin
Set #UpdateDate=(select UpdateDate from inserted)
End
Else
Set #UpdateDate=(select UpdateDate from deleted)
If #UpdateUser is null
Begin
Set #UpdateUser=(select UpdateUser from inserted)
End
Else
Set #UpdateUser=(select UpdateUser from deleted)
Insert Into UpdateRows
Select 'Orders', id,#UpdateDate,#UpdateUser,
convert(nvarchar,InvoiceID,1)+'_'+
convert(nvarchar,OrderID,1)+'_'+
MatterName+'_'+
convert(nvarchar,PrintID,1)+'_'+
OrderName+'_'+
CONVERT(nvarchar,lenght,1)+'_'+
CONVERT(nvarchar,Wide,1)+'_'+
CONVERT(nvarchar,Quantity,1)+'_'+
CONVERT(nvarchar,EnterDate,101)+'_'+
CONVERT(nvarchar,EndDate,101)
From deleted
thanks HLGEM
First, you never ever write a sql server trigger where you set the values of a scalar parameter to something from inserted or deleted. These tables can contain multiple rows. The trigger must account for that. – HLGEM

default values in mysql table

I have wrriten a vbs macro to automatically insert values from a DDE Excel sheet to a DB table.
The problem, is that some of the cells are empty
so the query turns out :
INSERT INTO `stock_realtime` (`fkstock`, `benefit_month`)
VALUES (77, '')
ON DUPLICATE KEY UPDATE `benefit_month` = '', `created` = NOW();
But my sql table contains default values of '0' just for cases like this when the value is empty.
Still it errors Incorrect integer value:'' for column 'benefit_month' at row 1'
The other (full) queries work fine.
'' is NOT empty (numeric) value, NULL is.
You are explicitly setting a value for benefit_month. If you don't set a value for a field then the default value will be set.
That would set the default value for benefit_month:
INSERT INTO `stock_realtime` (`fkstock`)
VALUES (77)