mysql trigger on update: how to detect NULL values? - mysql

I try to update the porcentageComplete profil when users updates his profile:
statut field:
`statut` enum('Marié','En couple','Divorcé') DEFAULT NULL,
Trigger is:
DROP TRIGGER IF EXISTS update_user;
DELIMITER $$
CREATE TRIGGER update_user
BEFORE UPDATE
ON users FOR EACH ROW
BEGIN
DECLARE porcentage int default 0;
IF NEW.statut!=NULL OR OLD.statut!=NULL THEN
set porcentage = porcentage + 1;
END IF;
SET NEW.porcentageCompleted = porcentage;
END$$
This not update correctly the porcentageCompleted : it looks like NULL comparaison does not work correctly

Comparisons with = or <>(or !=) to a NULL value yield NULL themselves, so not true, i.e. false. Use IS NULL and IS NOT NULL to check for NULLs.
...
IF NEW.statut IS NOT NULL
OR OLD.statut IS NOT NULL THEN
...
Worth a read: 3.3.4.6 Working with NULL Values

Related

Changing the value of a field from a table when another is changed using triggers

Good morning everyone.
I have a table like this in mysql:
CREATE TABLE NC
(
ID INTEGER AUTO_INCREMENT NOT null,
reportingDate DATE,
closingDate DATE,
State VARCHAR(20),
PRIMARY KEY(CodNC)
)
What I need is to automatically changing the value of the field "ClosingDate" up to today everytime the value of the field "State" is changed with "Closed" using TRIGGERS.
Thanks
This looks like a simple before update trigger:
delimiter //
create trigger trg_nc_before_state_update
before update on nc
for each row
begin
if new.state = 'Closed' and not old.state <=> 'Closed' then
set new.closingdate = current_date;
end if;
end
//
delimiter ;

How to check NEW value exists in BEFORE UPDATE Trigger Mysql

I have table Demo:
create table Demo (
id int(10) auto_increment primary key,
text varchar(30) default null,
istrue boolean default false
);
I want create a update trigger such that:
When Update Demo set istrue = false where id = 1; (not "set text = ") -> normal update.
When Update Demo set text= 'abc' where id = 1; ("set text = ") -> SET NEW.text := concat(OLD.text,'#', NEW.text);
I have implemented trigger as follows:
DELIMITER $$
CREATE TRIGGER update_text
BEFORE update ON Demo
FOR EACH ROW
BEGIN
IF (exists(SELECT NEW.text)) THEN -- how to check NEW.text exists???
SET NEW.text := concat(OLD.text,'#', NEW.text);
END IF;
END$$
DELIMITER ;
But it isn't working! Please help. Thank You!!! (My English is not good. Hope everyone sympathized)
CREATE TRIGGER update_text
BEFORE update
ON Demo
FOR EACH ROW
SET NEW.text = CASE WHEN OLD.text <=> NEW.text
THEN NEW.text
ELSE CONCAT(OLD.text, '#', NEW.text)
END;
PS. Single-statement trigger - BEGIN-END and DELIMITER reassing not needed.
After the deep investigation I have found the way to detect that the field value was set in UPDATE statement.
However, I can't guarantee its reliability. I only check does the substring '`text`' is present in current query text - but there is a lot of situations when this check will give errorneous result, for example, wrong TRUE if this substring is a part of new value, or wrong FALSE if the fieldname is not wrapped with backticks. So be aware.
CREATE TRIGGER update_text
BEFORE update
ON demo
FOR EACH ROW
SET NEW.`text` = CASE WHEN LOCATE('`text`', ( SELECT info
FROM information_schema.processlist
WHERE id = CONNECTION_ID()
)
)
THEN CONCAT(OLD.`text`, '#', NEW.`text`)
ELSE NEW.`text`
END;
fiddle

How to get column's DEFAULT in trigger?

I'd like one column's value to be forced to DEFAULT in trigger by some conditions.
In example below, if b IS NULL, a should be set with defined DEFAULT (that is 'SYSTEM').
CREATE TABLE `t_default` (
`n` VARCHAR(50) NOT NULL
, `a` VARCHAR(50) NOT NULL DEFAULT 'SYSTEM'
, `b` VARCHAR(50) NULL DEFAULT NULL
-- To moderators: please do not edit my formatting :)
);
DELIMITER //
CREATE TRIGGER `TRG_t_default_BeforeInsert` BEFORE INSERT ON `t_default` FOR EACH ROW BEGIN
IF NEW.b IS NULL THEN
SET NEW.a = DEFAULT(a);
END IF;
END
//
DELIMITER ;
Unfortunately, this gives error message "Unknown column 'a' in 'field list'" on insert:
INSERT INTO t_default (n) VALUES ('1');
I've found similar question, however providing table name or db + table name does not work as well:
CREATE TRIGGER `TRG_t_default_BeforeInsert` BEFORE INSERT ON `t_default` FOR EACH ROW BEGIN
IF NEW.b IS NULL THEN
SET NEW.a = DEFAULT(t_default.a);
-- SET NEW.a = DEFAULT(test.t_default.a);
END IF;
END
In this case message is slightly different: "Unknown table 't_default' in field list"
Also, I tried to use back quotes around column name with no success.
So, is it possible to get column DEFAULT in trigger at all? I'm using MySQL 5.7.
Thanks.
P.S. Sure, I know that I can do SET NEW.a = 'SYSTEM';
You can try
IF NEW.b IS NULL THEN
SELECT COLUMN_DEFAULT INTO #def FROM information_schema.COLUMNS
WHERE table_schema = 'database_name'
AND table_name = 't_default'
AND column_name = 'a';
SET NEW.a = #def;
END IF;

Function that generate Code returns the same things

There is a MySQL function in our web system to generate Code. The structure of the code is
district_cd(length:2) + date(length:8) + sequence no(length:5,start at 1).<like : ab2016090800001>
The sequence no was saved in table and will be updated (+1) when generate a new code.
But sometimes it returned two same codes and makes us fall in trouble. Here are the captures to replicate this problem, I will attach the DDL after this.
Step 1.Client1->change to manual commit then generate a code, but do not commit.
SET autocommit = 0;
select * from applies;
select * from sequence where apply_date = "2016-09-08";
select nextval("ab");
insert into applies (apply_id,apply_no,created,district_cd) values (2,"ab2016090800002","ab",now());
select * from sequence where apply_date = "2016-09-08";
Step2.Client2->change to manual commit then generate a code, stuck as Client1 locked
SET autocommit = 0;
select * from applies;
select * from sequence where apply_date = "2016-09-08";
insert into applies (apply_id,apply_no,created,district_cd) values (3,"ab20160908123456780","ab",now());
Step3.Client1->commit;
commit;
select * from sequence where apply_date = "2016-09-08";
Step4.Client2->code was generated and two records appeared in sequence table
select * from sequence where apply_date = "2016-09-08";
capture of Step4
Step5.Client2->commit;one of the two records that appeared in sequence table was deleted.The codes generated are duplicated.
commit;
select * from sequence where apply_date = "2016-09-08";
select * from applies;
capture of Step5
※DDL
Table:applies (apply_no:save the code)
CREATE TABLE `applies` (
`apply_id` varchar(100) NOT NULL DEFAULT '',
`apply_no` varchar(100) NOT NULL DEFAULT '',
`district_cd` varchar(100) DEFAULT NULL,
`created` datetime DEFAULT NULL,
PRIMARY KEY (`apply_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Table:sequence (current_value:save current sequnce value)
CREATE TABLE `sequence` (
`district_cd` varchar(3) NOT NULL DEFAULT '',
`current_value` int(11) NOT NULL DEFAULT '0',
`apply_date` date NOT NULL DEFAULT '0000-00-00',
PRIMARY KEY (`district_cd`,`current_value`,`apply_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Function:currval->get current sequence value by district_cd
DELIMITER ;;
CREATE DEFINER=`usr`#`%` FUNCTION `currval`(d VARCHAR(3)) RETURNS int(11)
DETERMINISTIC
BEGIN
DECLARE value INTEGER;
DECLARE needInitSequence INTEGER;
DECLARE today DATE;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET needInitSequence = 1;
SET value = 0;
SET today = current_date();
SELECT `current_value` INTO value
FROM `sequence`
WHERE `district_cd` = d AND `apply_date` = today limit 1;
IF needInitSequence = 1 THEN
INSERT INTO `sequence` (`district_cd`, `current_value`, `apply_date`) VALUES (d, value, today);
END IF;
RETURN value;
END
;;
DELIMITER ;
Function:nextval->generate code by district_cd
DELIMITER ;;
CREATE DEFINER=`usr`#`%` FUNCTION `nextval`(d VARCHAR(3)) RETURNS varchar(16) CHARSET utf8
DETERMINISTIC
BEGIN
DECLARE value INTEGER;
SET value = currval(d);
UPDATE `sequence`
SET `current_value` = `current_value` + 1
WHERE `district_cd` = d AND `apply_date` = current_date();
RETURN concat(d, date_format(now(), '%Y%m%d'), LPAD(currval(d), 5, '0'));
END
;;
DELIMITER ;
Triggers of applies->a business logic,if the length of apply_no is greater than 18,it will call the function:nextval to generate a new code
DELIMITER ;;
CREATE TRIGGER `convert_long_no` BEFORE INSERT ON `applies` FOR EACH ROW BEGIN
IF ((SELECT LENGTH(NEW.apply_no)) >= 18) THEN
SET NEW.apply_no = (SELECT nextval(NEW.district_cd));
END IF;
END
;;
DELIMITER ;
My Questions:
Why did the function:nextval returns two same codes?
Why did two records appear in sequnce when update the record.

MySQL - Update a column after information get's inserted

I have a table in MySql table like the following
row_timestamp timestamp
, row_id int(11) auto_increment
, mrn char(17)
, patients_last_name varchar(50)
, patients_first_name varchar(50)
, ssn char(4) default '0000'
, visit_key NULL
upon the insertion of a record, I'd like visit_key to bet set to visit_key = concat(mrn, row_id) I was trying to accomplish this with a before insert trigger to no avail, I kept getting that the mrn column was not in the field select list.
Update
I tried the following, which seems not to work because the auto_increment has not yet incremented:
set new.visit_key = concat(new.mrn, new.row_id)
I also tried
set new.visit_key = concat(new.mrn, max(row_id)+1)
I am thinking of the trigger to sort of act like a calculated field in MS Access, is this reasonable? Thoughts? Would it not be possible to do since the visit_key would technically be NULL and you cannot update a new value?
UPDATE
I used the following code that I adapted from this question here
DELIMITER //
CREATE TRIGGER vkt AFTER INSERT ON demographic_information
FOR EACH ROW
BEGIN
UPDATE `demographic_information` SET visit_key_test = concat(new.mrn, mew.row_id) WHERE row_id = NEW.row_id;
END;
//
DELIMITER ;
and got the following error message:
INSERT INTO `manchuco_nys_trauma`.`demographic_information` (
`row_timestamp` ,
`row_id` ,
`mrn` ,
`patients_last_name` ,
`patients_first_name` ,
`ssn` ,
`visit_id` ,
`visit_key_test`
)
VALUES (
CURRENT_TIMESTAMP , NULL , '123456', 'Sanderson', 'Steven', '1234', '12345670', NULL
)
MySQL said: Documentation
#1442 - Can't update table 'demographic_information' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
Thank you,
What you observe seems normal: since you're using BEFORE INSERT the id value doen't exist yet.
And the same applies for your try with concat(new.mrn, **new**.row_id): NEW has no sense at the moment.
So I suggest you use AFTER INSERT instead.
Try selecting the auto increment value inside the trigger:
CREATE TRIGGER trigger_name AFTER INSERT ON yourTable FOR EACH ROW
BEGIN
DECLARE next_id integer;
SET next_id = (SELECT AUTO_INCREMENT
FROM information_schema.TABLES
WHERE TABLE_SCHEMA='yourDB' AND TABLE_NAME='yourTable');
SET new.visit_key = CONCAT(new.mrn, next_id);
END
I discovered this approach in this SO article.