我有 Mysql 存储过程,在某些情况下会给出这样的信号错误:
CREATE DEFINER=`root`@`localhost` PROCEDURE `InsertAction`(IN `EmployerID` INT, IN `StoreFromID` INT, IN `StoreToID` INT, IN `StoreID` INT, IN `ProductID` INT, IN `Quantity` DECIMAL(10,2), IN `DualOperation` TINYINT, IN `inOrOut` TINYINT)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
START TRANSACTION;
SELECT @lastActionID;
SELECT @lastTransferID;
SELECT @retval;
SELECT SUM(ad.Quantity) INTO @retVal FROM productin pri JOIN actiondetails ad ON ad.ID=pri.ID;
IF DualOperation = 1
THEN
IF @retVal>Quantity
THEN
INSERT INTO Actions (EmployerID, StorehouseID, `Date`)
VALUES (EmployerID, StoreFromID, CURDATE());
SET @lastActionID = (SELECT ID FROM Actions ORDER BY ID DESC LIMIT 1);
INSERT INTO ProductTransfer (ID, TransferType)
VALUES (@lastActionID, 0);
INSERT INTO ActionDetails (ID,ProductID, Quantity)
VALUES (@lastActionID, ProductID, Quantity);
SET @lastTransferID = (SELECT ID FROM ProductTransfer ORDER BY ID DESC LIMIT 1);
INSERT INTO Actions (EmployerID, StorehouseID, `Date`) VALUES (EmployerID, StoreToID, CURDATE());
SET @lastActionID = (SELECT ID FROM Actions ORDER BY ID DESC LIMIT 1);
INSERT INTO ProductTransfer (ID, TransferType, ParentID) VALUES (@lastActionID, 1, @lastTransferID);
INSERT INTO ActionDetails (ID,ProductID, Quantity)
VALUES (@lastActionID, ProductID, Quantity);
ELSE
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Not enough materials';
END IF;
ELSE
INSERT INTO Actions (EmployerID, StorehouseID, `Date`)
VALUES (EmployerID, StoreID, CURDATE());
SET @lastActionID = (SELECT ID FROM Actions ORDER BY ID DESC LIMIT 1);
INSERT INTO ActionDetails (ID, ProductID, Quantity)
VALUES (@lastActionID, ProductID, Quantity);
IF InOrOut = 0
THEN
INSERT INTO ProductIn (ID, OrganizationID) values (@lastActionID, NULL);
ELSE
IF @retVal>Quantity
THEN
INSERT INTO ProductOut (ID, OrganizationID) values (@lastActionID, NULL);
ELSE
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Not enough materials';
END IF;
END IF;
END IF;
COMMIT;
END
当我通过 Mysql 查询运行此代码时,一切似乎都运行良好。如果
@retVal<=Quantity
并且没有插入记录(按原样工作),它会发出“材料不足”的信号...
但是当我从 PHP 调用这个过程时,它根本不会给出任何错误。没有插入任何行,但我无法收到通知失败的消息...这是 php 代码:
$mysqli->query("CALL `InsertAction`('6', '1', '2', '0', '13', '431243241', '1', '0')");
the $mysqli->sqlstate
给出0000
。
我应该如何理解程序已完成或收到信号?
所以我真正想要的是,如果
@retVal<=Quantity
然后给 php 异常。这段代码打印出"string"
:
try {
$mysqli->query("CALL `InsertAction`(6, 1, 2, 0, 13, 431243241, 1, 0)");
}
catch (Exception $e){
echo "string";
}
MYSQLI 不会抛出异常,您必须使用老式的方式
$result = $mysqli->query("CALL InsertAction(6, 1, 2, 0, 13, 431243241, 1, 0)");
if ($result === false) {
echo $mysqli->error;
exit;
}
此外,第 6 个参数是小数 (10,2) 不是整数,您传递的数字不适合 10,2 如果我记得正确的话,意思是小数点前 8 位和小数点后 2 位,所以请尝试适合的数字喜欢
$result = $mysqli->query("CALL InsertAction(6, 1, 2, 0, 13, 4312432.41, 1, 0)");
上面代码中真正的问题是没有声明变量。所以我将此 Select 语句更改为 Declare 语句。
BEGIN
DECLARE lastActionID INT unsigned;
DECLARE lastTransferID INT unsigned;
DECLARE retval INT unsigned;
START TRANSACTION;
SELECT SUM(ad.Quantity) INTO retVal FROM productin pri JOIN actiondetails ad ON ad.ID=pri.ID;
IF DualOperation = 1
THEN
IF retVal>Quantity
THEN
INSERT INTO Actions (EmployerID, StorehouseID, `Date`)
VALUES (EmployerID, StoreFromID, CURDATE());
SET lastActionID = (SELECT ID FROM Actions ORDER BY ID DESC LIMIT 1);
INSERT INTO ProductTransfer (ID, TransferType)
VALUES (lastActionID, 0);
INSERT INTO ActionDetails (ID, ProductID, Quantity)
VALUES (lastActionID, ProductID, Quantity);
SET lastTransferID = (SELECT ID FROM ProductTransfer ORDER BY ID DESC LIMIT 1);
INSERT INTO Actions (EmployerID, StorehouseID, `Date`) VALUES (EmployerID, StoreToID, CURDATE());
SET lastActionID = (SELECT ID FROM Actions ORDER BY ID DESC LIMIT 1);
INSERT INTO ProductTransfer (ID, TransferType, ParentID) VALUES (lastActionID, 1, lastTransferID);
INSERT INTO ActionDetails (ID,ProductID, Quantity)
VALUES (lastActionID, ProductID, Quantity);
ELSE
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Not enough materials';
END IF;
ELSE
INSERT INTO Actions (EmployerID, StorehouseID, `Date`)
VALUES (EmployerID, StoreID, CURDATE());
SET @lastActionID = (SELECT ID FROM Actions ORDER BY ID DESC LIMIT 1);
INSERT INTO ActionDetails (ID, ProductID, Quantity)
VALUES (lastActionID, ProductID, Quantity);
IF InOrOut = 0
THEN
INSERT INTO ProductIn (ID, OrganizationID) values (lastActionID, NULL);
ELSE
IF retVal>Quantity
THEN
INSERT INTO ProductOut (ID, OrganizationID) values (lastActionID, NULL);
ELSE
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Not enough materials';
END IF;
END IF;
END IF;
COMMIT;
select true;
END
现在我可以得到
$mysqli->sqlstate
45000
并检查程序是否有错误
实际上,您需要做一些准备才能在 PHP 中捕获来自 MySql 的异常。
使用:
$driver = new mysqli_driver();
$driver->report_mode = MYSQLI_REPORT_ALL ^ MYSQLI_REPORT_INDEX;
PHP 端:
$driver = new mysqli_driver();
$driver->report_mode = MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT;
$dbh = new mysqli(...);
try {
$result = $dbh->query("CALL proc_test_2(2)");
} catch (Throwable $err) {
print_r([
"code" => $err->getCode(),
"msg" => $err->getMessage(),
"trace" => $err->getTraceAsString()
]);
}
MySQL 例程(在此我展示了 SQLSTATE 的“快捷方式”-ERR_CUSTOM):
DROP PROCEDURE IF EXISTS proc_test_2;
CREATE PROCEDURE proc_test_2 (IN p_id INT)
BEGIN
DECLARE ERR_CUSTOM CONDITION FOR SQLSTATE '45000';
IF (p_id > 10) THEN
SIGNAL ERR_CUSTOM SET MESSAGE_TEXT = 'ERR #1', MYSQL_ERRNO = 1;
END IF;
IF (p_id < 5) THEN
SIGNAL SQLSTATE '45001' SET MESSAGE_TEXT = 'ERR #2', MYSQL_ERRNO = 222;
END IF;
SELECT p_id;
END;
DROP PROCEDURE IF EXISTS proc_test;
CREATE
PROCEDURE proc_test (
IN p_id INT
)
BEGIN
DECLARE E_STATE CHAR(5) DEFAULT '00000';
DECLARE E_MSG TEXT DEFAULT 'SUCCESS';
DECLARE E_NUM, DIAG_CNT INT DEFAULT 0;
DECLARE ERR_CUSTOM CONDITION FOR SQLSTATE '45000';
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS DIAG_CNT = NUMBER;
GET DIAGNOSTICS CONDITION DIAG_CNT E_STATE = RETURNED_SQLSTATE, E_MSG = MESSAGE_TEXT, E_NUM = MYSQL_ERRNO;
SELECT CAST(JSON_OBJECT('e_state', E_STATE, 'e_msg', E_MSG, 'e_num', E_NUM) AS JSON) app_debug2;
END;
IF (p_id > 10) THEN
SIGNAL ERR_CUSTOM SET MESSAGE_TEXT = 'Invalid p_id', MYSQL_ERRNO = 1;
END IF;
IF (p_id < 5) THEN
SIGNAL SQLSTATE '45001' SET MESSAGE_TEXT = 'INVALID #2', MYSQL_ERRNO = 2;
END IF;
SELECT p_id;
END;
下一个:
CALL proc_test(5);
CALL proc_test(11);
MySQL日志表:
DROP TABLE IF EXISTS error_logs;
CREATE TABLE error_logs (
error_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
error_log JSON
);
MySQL例程:
DROP PROCEDURE IF EXISTS proc_test_3;
CREATE PROCEDURE proc_test_3(
IN p_id INT
)
BEGIN
DECLARE E_STATE CHAR(5) DEFAULT '00000';
DECLARE E_MSG TEXT DEFAULT 'SUCCESS';
DECLARE E_NUM, DIAG_CNT INT DEFAULT 0;
DECLARE ERR_CUSTOM CONDITION FOR SQLSTATE '45000';
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS DIAG_CNT = NUMBER;
GET DIAGNOSTICS CONDITION DIAG_CNT E_STATE = RETURNED_SQLSTATE, E_MSG = MESSAGE_TEXT, E_NUM = MYSQL_ERRNO;
INSERT INTO error_logs (error_date, error_log)
SELECT CURRENT_TIMESTAMP, CAST(JSON_OBJECT('e_state', E_STATE, 'e_msg', E_MSG, 'e_num', E_NUM) AS JSON);
RESIGNAL;
END;
IF (p_id > 10) THEN
SIGNAL ERR_CUSTOM SET MESSAGE_TEXT = 'Invalid p_id', MYSQL_ERRNO = 111;
END IF;
IF (p_id < 5) THEN
SIGNAL SQLSTATE '45001' SET MESSAGE_TEXT = 'INVALID #2', MYSQL_ERRNO = 222;
END IF;
SELECT p_id;
END;
PHP 端:
$driver = new mysqli_driver();
$driver->report_mode = MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT;
$dbh = new mysqli(...);
try {
$result = $dbh->query("CALL proc_test_3(2)");
} catch (Throwable $err) {
print_r([
"code" => $err->getCode(),
"msg" => $err->getMessage(),
"trace" => $err->getTraceAsString()
]);
}