Through phpmyadmin's interface, I created a stored procedure as follows:
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `sp_checkin_create`(IN `userid` INT(10), IN `organizationid` INT(10), IN `checkindate` DATETIME(6), IN `checkoutdate` DATETIME(6), IN `checkinid` INT(10))
NO SQL
Insert Into checkin Values(CheckInID, UserID, OrganizationID, CheckInDate, CheckOutDate) ON DUPLICATE
KEY UPDATE CheckInID=CheckInID, UserID=userid, OrganizationID=organizationid, checkindate=checkindate, CheckOutDate=checkoutdate$$
DELIMITER ;
How do I return LAST_INSERT_ID() from this procedure? I know the use-case of:
SELECT LAST_INSERTED_ID();
But i can't find a way to combine this query in the procedure without getting ambiguous errors.
Any help would be appreciated.
EDIT 1
Create table statement:
DROP TABLE IF EXISTS `checkin`;
CREATE TABLE IF NOT EXISTS `checkin` (
`CheckInID` int(11) NOT NULL AUTO_INCREMENT,
`UserID` int(11) NOT NULL,
`OrganizationID` int(11) DEFAULT NULL,
`CheckInDate` datetime DEFAULT NULL,
`CheckOutDate` datetime(6) NOT NULL,
PRIMARY KEY (`CheckInID`),
UNIQUE KEY `CheckInID` (`CheckInID`)
) ENGINE=MyISAM AUTO_INCREMENT=4407 DEFAULT CHARSET=latin1;
Formally:
DELIMITER $$
CREATE
DEFINER=`root`#`localhost`
PROCEDURE `sp_checkin_create` ( IN `userid` INT(10),
IN `organizationid` INT(10),
IN `checkindate` DATETIME(6),
IN `checkoutdate` DATETIME(6),
IN `checkinid` INT(10),
OUT inserted_id BIGINT )
NO SQL
BEGIN
Insert Into checkin
Values (CheckInID, UserID, OrganizationID, CheckInDate, CheckOutDate)
ON DUPLICATE KEY UPDATE
UserID=userid, OrganizationID=organizationid, checkindate=checkindate, CheckOutDate=checkoutdate;
SELECT LAST_INSERT_ID() INTO inserted_id;
END;
$$
DELIMITER ;
and
CALL sp_checkin_create(123, 456, '2021-01-01', '2021-01-01', 789, #last_inserted_id);
SELECT #last_inserted_id;
Really: LAST_INSERT_ID() is used only when autoincrement generates new value - so it makes no sense in both cases (both new row inserted with AI explicit assigning and ODKU fired).
I want to add two triggers to my mysql database, but it seems that both triggers are not working (although the mysql compiler accepts the syntax). I do not receive an error message when I insert new data rows that should be detected as wrong by the trigger. Do you know what the mistake could be?
Here is the code of the triggers:
USE `mydb`$$
DROP TRIGGER IF EXISTS `mydb`.`Teilnehmer_BEFORE_INSERT` $$
USE `mydb`$$
CREATE DEFINER = CURRENT_USER TRIGGER `mydb`.`Teilnehmer_BEFORE_INSERT` BEFORE INSERT ON `Teilnehmer` FOR EACH ROW
BEGIN
IF (DATEDIFF(CURRENT_DATE(),NEW.Geburtsdatum) < 6570) THEN
-- Throw Exception
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Age is less than 18 years!';
END IF; -- A semicolon (delimiter) is missing here
END
$$
DELIMITER ;
DELIMITER $$
CREATE TRIGGER `enforce_phone_check` BEFORE INSERT ON `Teilnehmer` FOR EACH ROW
BEGIN
IF (NEW.Telefonnummer REGEXP '^(\\+?[0-9]{1,4}-)?[0-9]{3,10}$' ) = 0 THEN
SIGNAL SQLSTATE '33455'
SET MESSAGE_TEXT = 'Your Phone Number is incorrect';
END IF;
END$$
DELIMITER ;
Here are the table definitions:
CREATE TABLE IF NOT EXISTS `mydb`.`Preis` (
`idPreis` INT NOT NULL,
`Menge` VARCHAR(45) NULL,
`Wert` INT NULL,
`Art` VARCHAR(45) NULL,
PRIMARY KEY (`idPreis`))
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `mydb`.`Teilnehmer` (
`idTeilnehmer` INT NOT NULL,
`email` VARCHAR(45) NULL,
`Preis_idPreis` INT NOT NULL,
`Vorname` VARCHAR(45) NULL,
`Nachname` VARCHAR(45) NULL,
`Strasse` VARCHAR(45) NULL,
`Hausnr.` VARCHAR(45) NULL,
`PLZ` INT(4) NULL,
`Ort` VARCHAR(64) NULL,
`Geburtsdatum` DATE NULL,
`Telefonnummer` VARCHAR(16) NULL,
`Bild_URL TEXT` TEXT(500) NULL,
`ts` timestamp,
PRIMARY KEY (`idTeilnehmer`, `Preis_idPreis`),
INDEX `fk_Teilnehmer_Preis1_idx` (`Preis_idPreis` ASC) VISIBLE,
CONSTRAINT `fk_Teilnehmer_Preis1`
FOREIGN KEY (`Preis_idPreis`)
REFERENCES `mydb`.`Preis` (`idPreis`),
UNIQUE Key `idTeilnehmer`(`idTeilnehmer`, `Nachname`))
ENGINE = InnoDB;
And here is a table entry that should throw an error for both trigger (but doesn't):
INSERT INTO `mydb`.`teilnehmer` (`idTeilnehmer`, `email`, `Preis_idPreis`, `Vorname`, `Nachname`, `Strasse`, `Hausnr.`, `PLZ`, `Ort`, `Geburtsdatum`, `Telefonnummer`, `Bild_URL TEXT`, `ts`) VALUES ('1', 'sdfsdf', '1', 'sdfsd', 'sdfsd', 'sdfsdf', '17', '9230', 'Flawil', '2005-06-06', '2342345', 'stsdfsdf', '2019-06-06');
My triger It works normally but if i use procedur in the insert I'm getting the error
1442 - Can't update table 'stock' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
bu if i use manuel insert into table panier Normal inserted does not show me the error :'(
i use it like that :
call temps;
call stock_panier(94,19,'tranche');
call panier_ins(22547153,6185,null,now());
my triger :
DELIMITER $$
CREATE TRIGGER Stock_cal BEFORE INSERT on panier
FOR EACH ROW BEGIN
DECLARE is_exist INT;
DECLARE is_exist1 INT;
DECLARE is_exist2 INT;
set is_exist = (select stock.qte_res_s from stock WHERE stock.code_s=new.code_s);
set is_exist1 = new.qte_p;
set is_exist2 = new.qte_p;
if (is_exist<is_exist1) OR (is_exist2<1) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '✘: Opération a été refusée.';
ELSE
UPDATE stock SET qte_res_s=qte_res_s-new.qte_p,qte_sor_s=qte_sor_s+new.qte_p WHERE new.code_s=stock.code_s ;
END if;
END$$
DELIMITER ;
my table temperer :
CREATE PROCEDURE `temps`()
BEGIN
call virifier_table_exist('panier_tm');
if (#table_exists)=0 THEN
CREATE TEMPORARY TABLE `panier_tm` (
`N_p` bigint(20) DEFAULT Null,
`N` bigint(20) DEFAULT null,
`Code_barr` varchar(100) DEFAULT NULL,
`Référence` varchar(40) DEFAULT NULL,
`Désignation` varchar(40) DEFAULT NULL,
`Prix_HT` varchar(20) DEFAULT NULL,
`Fournisseur` varchar(30) DEFAULT NULL,
`Magasin` varchar(10) DEFAULT NULL,
`Qte_Vent` varchar(5) DEFAULT NULL,
`code_s` bigint(20) DEFAULT NULL,
`code_d` bigint(20) DEFAULT NULL,
tva_p varchar(5) DEFAULT Null,
type_ver varchar(15) DEFAULT null);
SET #row_number:=0;
else
DROP TEMPORARY TABLE `panier_tm`;
CREATE TEMPORARY TABLE `panier_tm` (
`N_p` bigint(20) DEFAULT Null,
`N` bigint(20) DEFAULT null,
`Code_barr` varchar(100) DEFAULT NULL,
`Référence` varchar(40) DEFAULT NULL,
`Désignation` varchar(40) DEFAULT NULL,
`Prix_HT` varchar(20) DEFAULT NULL,
`Fournisseur` varchar(30) DEFAULT NULL,
`Magasin` varchar(10) DEFAULT NULL,
`Qte_Vent` varchar(5) DEFAULT NULL,
`code_s` bigint(20) DEFAULT NULL,
`code_d` bigint(20) DEFAULT NULL,
tva_p varchar(5) DEFAULT Null,
type_ver varchar(15) DEFAULT null);
SET #row_number:=0;
end if;
END$$
DELIMITER ;
my procedure :
drop PROCEDURE if EXISTS panier_ins ;
DELIMITER $$
CREATE PROCEDURE panier_ins (in code_cccp varchar(30), code_cl1 bigint(20),N_p1 bigint(20),dat date)
BEGIN
DECLARE code_faa varchar(20);
DECLARE n_p2 bigint(20);
START TRANSACTION;
if (SELECT COUNT(Qte_Vent) from panier_tm tm2,stock WHERE tm2.code_s=stock.code_s and tm2.Qte_Vent>stock.qte_res_s and not EXISTS (select 1 from panier_vu vu2 where tm2.N_p=vu2.code_p and tm2.code_s=vu2.code_s ))>0 THEN
SELECT "Veuillez vous assurer que le stock est terminé" AS message ;
ELSE
set #count_n_p=(select COUNT(n_p) from panier_tm where n_p=n_p1);
set #n_p=(select n_p from panier_tm where n_p=n_p1);
if (#n_p) is null OR (#count_n_p)=0 THEN
SET #n_p2=(SELECT MIN(t1.code_p+1) as id FROM panier t1 LEFT JOIN panier t2 On t1.code_p+1=t2.code_p Where t2.code_cl IS NULL);
set #n_p2=if(#n_p2 = null,1,#n_p2);
INSERT INTO `panier`(`code_p`, `n_p`, `code_s`, `code_cl`, `type_ver_p`, `date_p`, `qte_p`, `prix_p`, `tva_p`) SELECT #n_p2,tm1.n,stock.code_s,code_cl1,type_ver,dat,qte_vent,prix_ht,tva_p FROM `preduit`,`stock`,`panier_tm` tm1 WHERE preduit.code_pr=stock.code_pr and tm1.code_s=stock.code_s and NOT EXISTS (select 1 from panier_vu vu1 where tm1.N_p=vu1.code_p and tm1.code_s=vu1.code_s);
update panier_tm set N_p=#n_p2;
set code_faa=(SELECT MIN(SUBSTRING(t1.code_fa, 6, length(t1.code_fa)-5)+1) as id FROM facture t1 LEFT JOIN facture t2 On SUBSTRING(t1.code_fa, 6, length(t1.code_fa)-5)+1=SUBSTRING(t2.code_fa, 6, length(t2.code_fa)-5) Where t2.code_fa IS NULL);
set code_faa= CONCAT( DATE_FORMAT(now(),"%Y"),"/",code_faa) ;
INSERT INTO `facture`(`autot`, `code_fa`, `code_ccp`, `code_cl`, `code_p`, `date_fa`, `type_ver`) VALUES (null,code_faa,code_cccp,code_cl1,#n_p2,now(),typ);
COMMIT ;
ELSE
START TRANSACTION;
INSERT INTO `panier`(`code_p`, `n_p`, `code_s`, `code_cl`, `type_ver_p`, `date_p`, `qte_p`, `prix_p`, `tva_p`) SELECT tm1.n_p,tm1.n,stock.code_s,code_cl1,type_ver,dat,qte_vent,prix_ht,tva_p FROM `preduit`,`stock`,`panier_tm` tm1 WHERE preduit.code_pr=stock.code_pr and tm1.code_s=stock.code_s and NOT EXISTS (select 1 from panier_vu vu1 where tm1.N_p=vu1.code_p and tm1.code_s=vu1.code_s);
DELETE b FROM `panier` b LEFT JOIN panier_tm f ON f.code_s = b.code_s and b.code_p=f.N_p WHERE code_p=n_p1 and f.N_p is null;
UPDATE `panier` p1,panier_tm tms SET p1.`n_p`=tms.N_p,p1.`qte_p`=tms.Qte_Vent,p1.`prix_p`=tms.Prix_HT,`tva_p`=tva1 WHERE p1.code_p=N_p1 and p1.code_s=tms.code_s;
COMMIT ;
END if;
END IF;
END$$
DELIMITER ;
Your problem are the two inserts into panier that depend on stock, while you have an on insert-trigger on panier that modifies the table stock that the insert into panier depends on:
INSERT INTO `panier`(`code_p`, ...) SELECT #n_p2,tm1.n,stock.code_s, ...
FROM `preduit`,`stock`,`panier_tm` tm1 WHERE reduit.code_pr=stock.code_pr ...
INSERT INTO `panier`(`code_p`, ...) SELECT tm1.n_p,tm1.n,stock.code_s, ...
FROM `preduit`,`stock`,`panier_tm` tm1 WHERE preduit.code_pr=stock.code_pr ...
They both have the structure
INSERT INTO panier (...) SELECT ... FROM stock ...
which is not allowed if you have an insert trigger on panier which modifies stock. These inserts are the statement which invoked this stored function/trigger in which you can't update table 'stock'. MySQL is for technical reasons not able to do this.
The straight forward technical workaround for this is to create a temporary table in which you store the result of your SELECT ..., and then insert into panier from that temporary table, e.g. something like
create temporary table tmp (...);
insert into tmp (...) select ... from stock ...;
insert into panier (...) select ... from tmp;
drop table tmp;
The insert-trigger now doesn't modify any dependent tables anymore, since you only select from tmp.
In your procedure, this would look like
create temporary table tmp (code_p bigint(20), n_p bigint(20),
..., tva_p varchar(5));
if (#n_p) is null OR (#count_n_p)=0 THEN
...
insert into tmp (code_p, n_p, ...)
SELECT #n_p2,tm1.n,stock.code_s, ...
FROM `preduit`,`stock`,`panier_tm` tm1 WHERE reduit.code_pr=stock.code_pr ...
insert into panier (code_p, n_p, ...) select code_p, n_p, ... from tmp;
...
else
...
insert into tmp (code_p, n_p, ...)
SELECT tm1.n_p,tm1.n,stock.code_s, ...
FROM `preduit`,`stock`,`panier_tm` tm1 WHERE preduit.code_pr=stock.code_pr ...
insert into panier (code_p, n_p, ...) select code_p, n_p, ... from tmp;
...
end if;
drop table tmp;
You can of course create the temporary table in your procedure temps.
It is also usually possible to redesign your procedure, triggers or logic so you do not need this, but that would probably be a larger endeavour.
And you should verify if that is actually the logic you want to apply, e.g. if you want to decide on the current state of stock which rows to insert, freeze this state (by putting it into the tmp-table) and then run the insert independently of how the stock-table changed during those inserts (e.g. if you would have inserted the 2nd row if you would have known what the first row changed in stock). I didn't check what your code is supposed to do, so this may or may not apply here, and may or may not be a problem here, but circular dependendies (and the error you get here because of it) can sometimes be a sign for an underlying problem.
I spent a lot of time on it, but I still can not resolve it.
For some reasons that I want prevent some same inserting to database. So I choose to use the trigger. That is,
CREATE TRIGGER trigger_card_usage_insert_block
BEFORE INSERT ON annual_card_usage_log
FOR EACH ROW
BEGIN
DECLARE lasttime INT; -- hold the last insert time
SELECT
usage_time INTO lasttime
FROM
annual_card_usage_log
WHERE
NEW.card_id = card_id
AND NEW.mch_id = mch_id
AND NEW.store_id = store_id
ORDER BY
usage_time DESC
LIMIT 1;
-- only insert when after 300 second
IF(
lasttime != NULL
AND(lasttime + 300 < new.usagetime)
) THEN
INSERT INTO annual_card_usage_log(
card_id ,
mch_id ,
store_id ,
usage_time
)
VALUES
(
NEW.card_id ,
NEW.mch_id ,
NEW.store_id ,
NEW.usage_time
);
END
IF;
END$
This is the creating code. And below is the table,
CREATE TABLE `annual_card_usage_log` (
`usage_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`card_id` int(10) unsigned NOT NULL COMMENT '',
`mch_id` int(10) unsigned NOT NULL COMMENT '',
`store_id` int(10) DEFAULT NULL,
`usage_time` int(10) NOT NULL COMMENT '',
PRIMARY KEY (`usage_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
Sadly I can not create the trigger, it seems wrong. Can you tell me why? Which code is wrong.
I use the mysql client called Sequel Pro, and when I run the sql I got a error message "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 6 Execution stopped!" I don't now the reason.
Thanks to #anatol I solve it. I changed the way. I set the value to a invalid data. Then create a job to delete the invalid data.
Trigger can not insert into a same table because it will cause endless loop, like #anatol point out.
DELIMITER $$
CREATE TRIGGER triger_card_usage_insert_block BEFORE INSERT ON annual_card_usage_log FOR EACH ROW
BEGIN
DECLARE lasttime INT(10) ;
SET lasttime =(
SELECT
usage_time
FROM
annual_card_usage_log
WHERE
NEW.card_id = card_id
AND NEW.mch_id = mch_id
AND NEW.store_id = store_id
ORDER BY
usage_time DESC
LIMIT 1
) ;
IF (lasttime + 300 > new.usage_time) THEN
SET new.new_card_id = new.card_id;
SET new.card_id = 0 ;
END
IF ;
END$$
DELIMITER ;
I have the following tables
CREATE TABLE `trigger_root` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`p` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `trigger_test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`p` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
);
and the following triggers
DELIMITER ||
CREATE TRIGGER tit
BEFORE INSERT ON trigger_root
FOR EACH ROW
BEGIN
INSERT INTO trigger_test (p) values (NEW.p);
END ||
CREATE TRIGGER tdt
BEFORE delete ON trigger_root
FOR EACH ROW
BEGIN
delete from trigger_test where p=OLD.p;
END ||
DELIMITER ;
However if I use the following statement
replace into trigger_root(id,p) select id,p from trigger_root;
only the delete trigger is called. if i remove the delete trigger the insert trigger is called.
so it seems replace only triggers one but not both triggers
is that a general restriction or do I do something wrong?
I found the error. the insert nedds to be after rahter than before.