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".
Related
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
I have the following query which I am trying to run in MySQL. Basically I am trying to work out how to update the corresponding field which isn't null. I know its going to be one of two possible fields but I want to update the one which isn't empty. Maybe I am missing something blindingly obvious, but this is what I've tried thus far:
UPDATE `table`
#SET IF(FieldA IS NULL,FieldB,FieldA) = 1234
#SET IFNULL(FieldA,FieldB) = 1234
WHERE `FieldC` = '5678'
AND (
`FieldA` = '1234'
OR `FieldB` = '1234'
)
I suspect there may be a CASE solution but I'd prefer a shorthand/simple version option if it exists.
(My)SQL has no syntax that allows you to dynamically change the column you want to update, e.g. something like update ... set {PickFieldBaseOnCondition:FieldA|FieldB} = 1234. You have to specify a column there. The moment you start your query, the basic structure of the query (and all fields involved) have to be clear and fixed, only the values can change.
So you need to update both fields in your query if it shall be able to modify two different columns. But you can of course decide to just not modify the content of a field based on its content, e.g. keep it if it is null already:
update `table`
set FieldA = IF(FieldA IS NOT null, 1234, FieldA),
FieldB = IF(FieldB IS NOT null, 1234, FieldB)
where ...
Note that the requirement "update the other field if a field is null" only works if your initial condition that one field is null and one is not null is fulfilled, which you said is given. Otherwise, you should include a test if both fields are null or both fields are not null (in the comparison for the first field), which you could e.g. do with
update `table`
set FieldA = IF(FieldB IS null, 1234, null),
FieldB = IF(FieldB IS NOT null, 1234, FieldB)
where ...
FieldA can now be changed from content to null if both fields are not null (and from null to content if both fields are null), to enforce the condition that exactly one field is not null.
Please also note that IF() is a MySQL-only shorthand for CASE and doesn't work in all databases. You prefered a non-case solution, but it can trivially be rewritten using the sql-standard CASE.
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.
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
I'm trying to update a field of my table with the CONCAT of the some fields of the same table.
Whith this
UPDATE tabex SET field1=CONCAT(tabex.a1,', ',tabex.a2,', ',tabex.a3,', ',tabex.a4,', ',tabex.a5,', ',tabex.a6,', 'tabex.a7,', ',tabex.a8,', ',tabex.a9 );
This query has 0 rows affected and no errors.
With this other query
UPDATE tabex SET field1=CONCAT_WS(tabex.a1,', ',tabex.a2,', ',tabex.a3,', ',tabex.a4,', ',tabex.a5,', ',tabex.a6,', 'tabex.a7,', ',tabex.a8,', ',tabex.a9 );
If the content of some of a(n) fields is NULL mysql puts a copy of the previous result
Someone can help me?
When this query
UPDATE tabex SET field1=CONCAT(tabex.a1,', ',tabex.a2,', ',tabex.a3,', ',tabex.a4,', ',tabex.a5,', ',tabex.a6,', 'tabex.a7,', ',tabex.a8,', ',tabex.a9 );
doesn't affect a row, the only explanation would be, that the table is empty. It would update every row in the table. But if one of the columns is NULL, your field1 column will also be NULL.
To avoid that, you have to use the COALESCE() function. This function returns the first of its parameters which is not NULL.
UPDATE tabex SET field1=CONCAT(COALESCE(tabex.a1, ''),', ',...);
On a sidenote I have to ask, why you want to do this. Comma separated values in columns are a bad idea most of the times.
And finally, your query using CONCAT_WS() is wrong. The _WS in the function name is short for "with separator", so the first parameter is the separator which then is placed between the other parameters of the function. So you should write it like this:
UPDATE tabex SET field1=CONCAT_WS(',', tabex.a1, tabex.a2, tabex.a3,...);
Another advantage of the CONCAT_WS() function is, that it ignores NULL values. Read more about the two functions in the manual.