MySql error 1064 on trigger createion with if statement - mysql

Below is the code. All I want to do is check if a quote exists and, if not, insert the record into another table.
DROP TRIGGER IF EXISTS `CB2`;
CREATE TRIGGER CB2
AFTER UPDATE
ON `quotes` FOR EACH ROW
BEGIN
IF (SELECT quoteID FROM booking WHERE quoteID <> new.`quoteID`) THEN
INSERT INTO `booking`(`Book_ID`, `Date`, `CustomerID`, `CustodianID`, `cusCntNum`, `Service`, `sAddress`, `Size`, `Comments`, `Frequency`, `Duration`, `Bdrms`, `Bathrm`, `Living Spaces`, `AppointmentStartDate`, `Time`, `ServiceDay`, `AddOns`, `Fee`, `quoteID`, `uBookingID`) VALUES (NULL, CURRENT_DATE, new.CustAccNum, new.CustodianNum, new.Contact_Number, new.ServType,new.Address, new.CommercialSize, new.Comments, new.Frequency, new.Duration, new.Bedrooms, new.Bathrooms, new.lSpaces, new.AppointmentDate, new.AppointmentTime ,DAYOFWEEK(new.AppointmentTime), new.sAddOns, new.Fee, new.quoteID,'');
END IF;
END

You need DELIMITER so that mysql can identify whyt belongs to the trigger
DROP TRIGGER IF EXISTS `CB2`;
DELIMITER //
CREATE TRIGGER CB2
AFTER UPDATE
ON `quotes` FOR EACH ROW
BEGIN
IF (SELECT quoteID FROM booking WHERE quoteID <> new.`quoteID`) THEN
INSERT INTO `booking`
(`Book_ID`, `Date`, `CustomerID`, `CustodianID`, `cusCntNum`, `Service`, `sAddress`, `Size`, `Comments`, `Frequency`, `Duration`, `Bdrms`, `Bathrm`, `Living Spaces`, `AppointmentStartDate`, `Time`, `ServiceDay`, `AddOns`, `Fee`, `quoteID`, `uBookingID`) VALUES
(NULL, CURRENT_DATE, new.CustAccNum, new.CustodianNum, new.Contact_Number, new.ServType,new.Address, new.CommercialSize, new.Comments, new.Frequency, new.Duration, new.Bedrooms, new.Bathrooms, new.lSpaces, new.AppointmentDate, new.AppointmentTime ,DAYOFWEEK(new.AppointmentTime), new.sAddOns, new.Fee, new.quoteID,'');
END IF;
END//
DELIMITER ;

You do need to set delimiters but you also need an existence check. Using a simplified version of your model
drop table if exists quotes,booking;
create table quotes(quoteid int, val int);
create table booking(quoteid int,val int);
drop trigger if exists t;
delimiter $$
CREATE TRIGGER t
AFTER UPDATE
ON `quotes` FOR EACH ROW
BEGIN
insert into debug_table(msg) values (new.quoteid);
IF not exists (SELECT quoteID FROM booking WHERE quoteID = new.`quoteID`) THEN
INSERT INTO `booking`( `quoteID`,val) VALUES (new.quoteid,new.val);
END if;
end $$
delimiter ;
truncate debug_table;
insert into quotes(quoteid) values (1),(2);
update quotes set val = 10 where quoteid = 1;
update quotes set val = 20 where quoteid = 1;
MariaDB [sandbox]> select * from booking;
+---------+------+
| quoteid | val |
+---------+------+
| 1 | 10 |
+---------+------+
1 row in set (0.001 sec)
MariaDB [sandbox]>
MariaDB [sandbox]> select * from debug_table;
+----+------+
| id | msg |
+----+------+
| 1 | 1 |
| 2 | 1 |
+----+------+
2 rows in set (0.001 sec)
You don't need the debug table but it provides proof that the trigger fired twice as expected.
BTW I'm not convinced that your logic is sound.

Related

How to make triggers for delete and adding with if statement MySQL?

have two table's Queue (appointment_id, actual_time) Queue_Summary (date, doctor_id, num_of_patients)
The first is all the queues there are and the second is how many queues for each doctor on a certain date. I need to build a trigger that updates the num_of_patients, every time in Queue that a queue is added I need to add to a doctor num_of_patients on that date. Also when removing.
I have just counted the number of queues given a doctor_id and date, made it into two triggers.
But the only problem I have is where do I place the if statement that checks if this date is on Queue_Summary and if not adds it.
(P.S - Im not 100% on thoes also as my database is a bit off and does tons of problems, if there are any problem in thoes statments I'll be more them happy to know)
delimiter //
CREATE TRIGGER update_queue_summary
AFTER DELETE ON queue
FOR EACH ROW
BEGIN
update queue_summary as qs set num_of_patient = (
select count(appointment_id)
from queue as q join appointment as a on appointment_id
where a.doctor_id=qs.doctor_id and date(qs.actual_time)=date(qs.date())
group by appointment_id
) where doctor_id=qs.doctor_id and date(qs.actual_time)=date(qs.date());
END;//
delimiter ;
delimiter //
CREATE TRIGGER update_queue_summary
AFTER insert ON queue
FOR EACH ROW
BEGIN
update queue_summary as qs set num_of_patient = (
select count(appointment_id)
from queue as q join appointment as a on appointment_id
where a.doctor_id=qs.doctor_id and date(qs.actual_time)=date(qs.date())
group by appointment_id
) where doctor_id=qs.doctor_id and date(qs.actual_time)=date(qs.date());
END;//
delimiter ;
You should carry out an existence test in your trigger. For example
drop table if exists queue,queue_summary;
create table queue (appointment_id int auto_increment primary key, doctor_id int,actual_time datetime);
create table Queue_Summary (date date, doctor_id int, num_of_patients int);
delimiter $$
create trigger ut after insert on queue
for each row
begin
if not exists (select 1 from queue_summary where date = date(new.actual_time) and doctor_id = new.doctor_id) then
insert into queue_summary values(date(new.actual_time),new.doctor_id,1);
else
update queue_summary
set num_of_patients = num_of_patients + 1
where date = date(new.actual_time) and doctor_id = new.doctor_id;
end if;
end $$
delimiter ;
insert into queue (doctor_id,actual_time) values(1,'2020-05-03 09:00'),(1,'2020-05-03 09:30');
select * from queue;
select * from queue_summary;
MariaDB [sandbox]> select * from queue;
+----------------+-----------+---------------------+
| appointment_id | doctor_id | actual_time |
+----------------+-----------+---------------------+
| 1 | 1 | 2020-05-03 09:00:00 |
| 2 | 1 | 2020-05-03 09:30:00 |
+----------------+-----------+---------------------+
2 rows in set (0.001 sec)
MariaDB [sandbox]> select * from queue_summary;
+------------+-----------+-----------------+
| date | doctor_id | num_of_patients |
+------------+-----------+-----------------+
| 2020-05-03 | 1 | 2 |
+------------+-----------+-----------------+
1 row in set (0.001 sec)
And a delete trigger is similar but simpler
delimiter $$
create trigger dt after delete on queue
for each row
begin
if exists (select 1 from queue_summary where date = date(OLD.actual_time) and doctor_id = old.doctor_id) then
update queue_summary
set num_of_patients = num_of_patients - 1
where date = date(old.actual_time) and doctor_id = old.doctor_id;
end if;
end $$
delimiter ;
The existence check is entirely cosmetic since a delete won't complain if there is nothing to delete.

Unable to create procedure for inserting data in two tables

I have two tables tbl_pattern and tbl_pattern_features
I would like to insert the data into tbl_pattern_features against the last_insert_id of tbl_pattern.
Here is the query:
BEGIN
DROP INDEX fld_pattern_name ON tbl_pattern;
CREATE INDEX fld_pattern_name ON tbl_pattern (fld_pattern_name);
DECLARE #NewID INT(10)
IF pAction = "INSERT" THEN
INSERT INTO tbl_pattern(fld_pattern_name, fld_pattern_category, fld_pattern_utqg, pattern_image, fld_pattern_title, fld_pattern_description)
VALUES (ppattern_name, ppattern_category, ppattern_utqg, ppattern_image, ppattern_title, ppattern_descrip);
SELECT #NewID = SCOPE_IDENTITY();
INSERT INTO tbl_pattern_features(fld_pattern_id, fld_feature_name, fld_feature_description, feature_image)
VALUES (#NewID, ffeature_name, ppattern_utqg, ffeature_descrip, ffeature_image);
END IF;
END
MySQL doesn't have SCOPE_IDENTITY(). The function you are looking for is LAST_INSERT_ID()
SELECT LAST_INSERT_ID() as id
will return
+----+
| id |
+----+
| 1 |
+----+

TRIGGER with insert partial field

2 tables
tb_product :
product_id,
product_size_values
tb_product_category :
product_id ,
category_id
product_size_values contains '41,42,46' or '44,45,46,47' or any values from 42 to 48 with coma separator ....
and the trigger
CREATE TRIGGER trg_product_size AFTER UPDATE ON tb_product
FOR EACH ROW
BEGIN
IF (NEW.product_size_values != OLD.product_size_values) THEN
INSERT INTO tb_product_category (product_id, product_size_values)
VALUES (NEW.product_id, 42) WHERE NEW.product_size_values LIKE '%42%'
;
INSERT INTO tb_product_category (product_id, product_size_values)
VALUES (NEW.product_id, 43) WHERE NEW.product_size_values LIKE '%43%'
;
END IF;
END //
of course the WHERE NEW.product_size_values LIKE does not work
my normal query to do that actually (without trigger ) is
insert into tb_product_category (product_id,category_id)
select product_id,42
from tb_product
where product_size_values like '%42%' ;
insert into tb_product_category (product_id,category_id)
select product_id,43
from tb_product
where product_size_values like '%43%' ;
and NO the comma separated field cannot be changed
thanks for helping
I guess the problem is that you cannot use a like test in a conditional statement and you cannot use a where clause with values and an insert select seems wasteful. You may be able to ditch the like clause and use and instr or locate to check values for example
drop table if exists tb_product,tb_product_category;
create table tb_product
(product_id int,
product_size_values varchar(20));
insert into tb_product values
(1,'42');
create table tb_product_category
(product_id int,
category_id int);
drop trigger if exists trg_product_size;
delimiter $$
CREATE TRIGGER trg_product_size AFTER UPDATE ON tb_product
FOR EACH ROW
BEGIN
IF (NEW.product_size_values != OLD.product_size_values) THEN
if instr(NEW.product_size_values,42) > 0 then
INSERT INTO tb_product_category (product_id, category_id) values (new.product_id,42);
end if;
if INSTR(NEW.product_size_values,43) > 0 then
INSERT INTO tb_product_category (product_id, category_id) values (new.product_id,43);
end if;
END IF;
END $$
delimiter ;
select product_size_values from tb_product;
update tb_product
set product_size_values = '42,43' where product_id = 1;
select * from tb_product_Category;
+------------+-------------+
| product_id | category_id |
+------------+-------------+
| 1 | 42 |
| 1 | 43 |
+------------+-------------+
Works as coded. You may wish to change the insert logic to check for existence before inserting to avoid duplicates.

count number of hierarchical childrens in sql

I have a table that stores parent and left child and right child information. How do i count number of children belongs that parent?
for example my table structure is:
parent left right
--------------------
1 2 3
3 4 5
4 8 9
5 10 11
2 6 7
9 12 null
How do I count number of sub nodes for any parent. For example 4 contains following hierarchical child nodes - 8,9,12 so number of children are 3.
3 contains following sub nodes -> 4,5,10,11,8,9,12 so total number of children 7.
How do I achieve this using SQL query?
create table mytable
( parent int not null,
cleft int null,
cright int null
)
insert into mytable (parent,cleft,cright) values (1,2,3);
insert into mytable (parent,cleft,cright) values (2,6,7);
insert into mytable (parent,cleft,cright) values (3,4,5);
insert into mytable (parent,cleft,cright) values (4,8,9);
insert into mytable (parent,cleft,cright) values (5,10,11);
insert into mytable (parent,cleft,cright) values (6,null,null);
insert into mytable (parent,cleft,cright) values (7,null,null);
insert into mytable (parent,cleft,cright) values (8,13,null);
insert into mytable (parent,cleft,cright) values (9,12,null);
insert into mytable (parent,cleft,cright) values (10,null,null);
insert into mytable (parent,cleft,cright) values (12,null,null);
insert into mytable (parent,cleft,cright) values (13,null,17);
insert into mytable (parent,cleft,cright) values (17,null,null);
DELIMITER $$
CREATE procedure GetChildCount (IN parentID INT)
DETERMINISTIC
BEGIN
declare ch int;
declare this_left int;
declare this_right int;
declare bContinue boolean;
declare count_needs_scan int;
create temporary table asdf999 (node_id int,processed int);
-- insert into asdf999 (node_id,processed) values (1,0);
-- update asdf999 set processed=1;
SET ch = parentID;
set bContinue=true;
while bContinue DO
-- at this point you are sitting at a ch (anywhere in hierarchy)
-- as you are looping and getting/using children
-- save non-null children references: -----------------------------
select cleft into this_left from mytable where parent=ch;
if !isnull(this_left) then
insert asdf999 (node_id,processed) select this_left,0;
end if;
select cright into this_right from mytable where parent=ch;
if !isnull(this_right) then
insert asdf999 (node_id,processed) select this_right,0;
end if;
-- -----------------------------------------------------------------
select count(*) into count_needs_scan from asdf999 where processed=0;
if count_needs_scan=0 then
set bContinue=false;
else
select node_id into ch from asdf999 where processed=0 limit 1;
update asdf999 set processed=1 where node_id=ch;
-- well, it is about to be processed
end if;
END WHILE;
select count(*) as the_count from asdf999;
drop table asdf999;
END $$
DELIMITER ;
call GetChildCount(2); -- answer is 2
call GetChildCount(4); -- answer is 5
I could supply a version that creates a dynamically named table (or temp table) and clobbers it at end if you want . "dynamic sql / prepare statment" inside of a procedure. that way users won't step on each other with shared use of the work table asdf999. so this is not production ready. but the above gives you an idea of the concept

Mysql select statement only works on first iteration in while loop, insert working fine

Why is the SELECT statement inside the WHILE loop only returning value for the first iteration ?
Both of the INSERT IGNORE and the second INSERT is working, and are inserting rows equal to amount.
If I set amount to 10, I only get the results from the first inserted row. However, the procedure will INSERT amount rows to rand_strings and rand_strings_info tables.
The Procedure:
DROP PROCEDURE if exists test_while;
DELIMITER $$
CREATE PROCEDURE test_while(amount INT, description VARCHAR(255))
BEGIN
WHILE amount > 0 DO
INSERT IGNORE INTO rand_strings(rand_string) /*WORKS EVERY ITERATION*/
SELECT generate_rand_string(); /*function to generate a random string.*/
SELECT * FROM rand_strings WHERE id = LAST_INSERT_ID(); /*ONLY WORKS FIRST TIME */
INSERT INTO rand_strings_info(id, col2, col3) /*WORKS EVERY ITERATION*/
VALUES (LAST_INSERT_ID(), now(), description);
SET amount = amount - 1;
END WHILE;
END$$
DELIMITER ;
CALL test_while(10, 'This is the description of the string…')
RESULTS:
id | rand_string
1 | jgdlkjaht
Some interfaces do not show all the results as expected, but the code runs correctly.
You can see in the following SQL Fiddle that only shows the first record in the rand_strings table when stored procedure runs, but running the same code on the MySQL command line the result is as follows:
mysql> CALL `test_while`(5, 'This is the description of the string...');
+----+------------------------------------------+
| id | rand_string |
+----+------------------------------------------+
| 1 | f4c77a3155d95ad1e818b1b06a62deec8e0b6754 |
+----+------------------------------------------+
1 row in set (0.00 sec)
+----+------------------------------------------+
| id | rand_string |
+----+------------------------------------------+
| 2 | 7cbcca49596262836f5af91643303d10b3804900 |
+----+------------------------------------------+
1 row in set (0.00 sec)
+----+------------------------------------------+
| id | rand_string |
+----+------------------------------------------+
| 3 | 2ba2c7276c0b66e3dcbb971b7f54af9bced578a4 |
+----+------------------------------------------+
1 row in set (0.00 sec)
+----+------------------------------------------+
| id | rand_string |
+----+------------------------------------------+
| 4 | d52426b19a59c515b02268347c508383873d4d73 |
+----+------------------------------------------+
1 row in set (0.00 sec)
+----+------------------------------------------+
| id | rand_string |
+----+------------------------------------------+
| 5 | fbc25c6204b609e8f4f4f8a33b534bff9a011e5f |
+----+------------------------------------------+
1 row in set (0.00 sec)
Query OK, 1 row affected (0.00 sec)
UPDATE
DELIMITER $$
CREATE PROCEDURE `test_while`(`amount` INT, `description` VARCHAR(255))
BEGIN
DECLARE `_LAST_INSERT_ID`, `_first_inserted_in_this_run` INT UNSIGNED DEFAULT NULL;
CREATE TEMPORARY TABLE IF NOT EXISTS `temp_generate_rand_string` (
`insert_id` INT UNSIGNED PRIMARY KEY,
`first_inserted_in_this_run` INT UNSIGNED
) ENGINE=MEMORY;
WHILE `amount` > 0 DO
INSERT IGNORE INTO `rand_strings`(`rand_string`) /*WORKS EVERY ITERATION*/
SELECT `generate_rand_string`(); /*function to generate a random string.*/
SET `_LAST_INSERT_ID` := LAST_INSERT_ID();
IF (`_first_inserted_in_this_run` IS NULL) THEN
SET `_first_inserted_in_this_run` := `_LAST_INSERT_ID`;
END IF;
INSERT INTO `temp_generate_rand_string` (`insert_id`, `first_inserted_in_this_run`)
VALUES
(`_LAST_INSERT_ID`, `_first_inserted_in_this_run`);
-- SELECT * FROM `rand_strings` WHERE `id` = `_LAST_INSERT_ID`; /*ONLY WORKS FIRST TIME */
INSERT INTO `rand_strings_info`(`id`, `col2`, `col3`) /*WORKS EVERY ITERATION*/
VALUES (`_LAST_INSERT_ID`, NOW(), `description`);
SET `amount` := `amount` - 1;
END WHILE;
SELECT `rs`.`id`, `rs`.`rand_string`
FROM `rand_strings` `rs`
INNER JOIN `temp_generate_rand_string` `tgrs` ON
`tgrs`.`first_inserted_in_this_run` = `_first_inserted_in_this_run` AND
`rs`.`id` = `tgrs`.`insert_id`;
DELETE
FROM `temp_generate_rand_string`
WHERE `first_inserted_in_this_run` = `_first_inserted_in_this_run`;
END$$
DELIMITER ;
SQL Fiddle demo