存储过程成功但在 mysql 中 运行 不正确
Stored procedure successful but doesn't run properly in mysql
我有一个大约 2 table 秒的存储过程,如下所示:
CREATE DEFINER=`brambang`@`%` PROCEDURE `create_discount_campaign`(
IN discount_campaign_discount_type_id CHAR(22),
IN discount_campaign_product_id INT(10),
IN discount_campaign_marketing_target_id INT(10),
IN discount_campaign_max_use_per_user int(11),
IN discount_campaign_discount_code varchar(100),
IN discout_campaign_discount_amount decimal(10,0),
IN discount_campaign_start_date datetime,
IN discount_campaign_end_date datetime,
IN discount_campaign_min_order_quantity INT(11),
IN discount_campaign_min_order_price decimal(10,0),
IN discount_campaign_discount_quota INT(11),
IN discount_campaign_min_product_varian INT(11),
IN discount_campaign_apply_all_product INT(10),
IN discount_campaign_product_product_id int(10) unsigned,
IN discount_campaign_product_active tinyint(1),
IN discount_campaign_product_createdby int(11),
IN discount_campaign_product_updatedby int(11),
IN discount_campaign_product_category_id VARCHAR(100),
IN discount_campaign_advanced_discount_advanced_rules_id int(50),
IN discount_campaign_advanced_value varchar(255),
IN discount_campaign_advanced_status tinyint(4)
)
proc:
BEGIN
DECLARE is_dc_discount_type_id INT(10);
DECLARE is_dc_product_id INT(10);
DECLARE is_dc_marketing_target_id INT(10);
DECLARE is_dc_max_use_per_user INT(11);
DECLARE is_dc_discount_code varchar(100);
DECLARE is_dc_discount_amount decimal(10,0);
DECLARE is_dc_start_date datetime;
DECLARE is_dc_end_date datetime;
DECLARE is_dc_min_order_quantity INT(11);
DECLARE is_dc_min_order_price decimal(10,0);
DECLARE is_dc_discount_quota INT(11);
DECLARE is_dc_min_product_variant INT(11);
DECLARE is_dc_apply_all_product INT(10);
DECLARE is_dcp_product_id INT(10);
DECLARE is_dcp_active tinyint(1);
DECLARE is_dcp_createdby int(11);
DECLARE is_dcp_updatedby int(11);
DECLARE is_dca_discount_advanced_rules_id int(50);
DECLARE is_dca_value varchar(255);
DECLARE is_dca_status tinyint(4);
DECLARE LAST_INSERT_ID int(11);
DECLARE discount_campaign_product_category_id varchar(100);
SET @querie = NULL;
SET @querie2 = NULL;
SET @querie3 = NULL;
SET lc_time_names = "id_ID";
SET @is_dc_discount_type_id = NULL;
SET @is_dc_product_id = NULL;
SET @is_dc_marketing_target_id = NULL;
SET @is_dc_max_use_per_user = NULL;
SET @is_dc_discount_code = NULL;
SET @is_dc_discount_amount = NULL;
SET @is_dc_start_date = NULL;
SET @is_dc_end_date = NULL;
SET @is_dc_min_order_quantity = NULL;
SET @is_dc_min_order_price = NULL;
SET @is_dc_discount_quota = NULL;
SET @is_dc_min_product_variant = NULL;
SET @is_dc_apply_all_product = NULL;
SET @is_dcp_product_id = NULL;
SET @is_dcp_active = NULL;
SET @is_dcp_createdby = NULL;
SET @is_dcp_updatedby = NULL;
SET @is_dca_discount_advanced_rules_id = NULL;
SET @is_dca_value = NULL;
SET @is_dca_status = NULL;
SET @LAST_INSERT_ID = NULL;
SET @discount_campaign_product_category_id = NULL;
SET @is_dc_discount_type_id = discount_campaign_discount_type_id;
SET @is_dc_product_id = discount_campaign_product_id;
SET @is_dc_marketing_target_id = discount_campaign_marketing_target_id;
SET @is_dc_max_use_per_user = discount_campaign_max_use_per_user;
SET @is_dc_discount_code = discount_campaign_discount_code;
SET @is_dc_discount_amount = discout_campaign_discount_amount;
SET @is_dc_start_date = discount_campaign_start_date;
SET @is_dc_end_date = discount_campaign_end_date;
SET @is_dc_min_order_quantity = discount_campaign_min_order_quantity;
SET @is_dc_min_order_price = discount_campaign_min_order_price;
SET @is_dc_discount_quota = discount_campaign_discount_quota;
SET @is_dc_min_product_variant = discount_campaign_min_product_varian;
SET @is_dc_apply_all_product = discount_campaign_apply_all_product;
SET @discount_campaign_product_category_id = discount_campaign_product_category_id;
SET @querie = CONCAT("
INSERT INTO discount_campaigns (
discount_type_id,
product_id,
marketing_target_id,
max_use_per_user,
discount_code,
discount_amount,
start_date,
end_date,
min_order_quantity,
min_order_price,
discount_quota,
min_product_variant,
apply_all_products,
createdAt,
updatedAt
) VALUES (
?,
?,
?,
?,
?,
?,
?,
?,
?,
?,
?,
?,
?,
NOW(),
NOW()
);
");
PREPARE stmt FROM @querie;
EXECUTE stmt
USING
@is_dc_discount_type_id,
@is_dc_product_id,
@is_dc_marketing_target_id,
@is_dc_max_use_per_user,
@is_dc_discount_code,
@is_dc_discount_amount,
@is_dc_start_date,
@is_dc_end_date,
@is_dc_min_order_quantity,
@is_dc_min_order_price,
@is_dc_discount_quota,
@is_dc_min_product_variant,
@is_dc_apply_all_product;
DEALLOCATE PREPARE stmt;
SET @is_dcp_product_id = discount_campaign_product_product_id;
SET @is_dcp_active = discount_campaign_product_active;
SET @is_dcp_createdby = discount_campaign_product_createdby;
SET @is_dcp_updatedby = discount_campaign_product_updatedby;
SET @LAST_INSERT_ID = LAST_INSERT_ID();
IF (discount_campaign_apply_all_product = 0) THEN
SET @querie2 = CONCAT("INSERT INTO discount_campaign_product (discount_campaign_id,
product_id,
active,
createdby,
updatedby,
createdAt,
updatedAt
) SELECT ?,product_id,?,?,NULL,NOW(),NULL
FROM product_categories WHERE category_id IN
(?) AND status=1");
PREPARE stmt_2 FROM @querie2;
EXECUTE stmt_2
USING
@LAST_INSERT_ID,
@is_dcp_active,
@is_dcp_createdby,
@discount_campaign_product_category_id;
DEALLOCATE PREPARE stmt_2;
END IF;
END
基本上我有 2 tables discount_campaign(任何带有别名 dc 的变量都是 table) 和 discount_campaign_product(任何带有别名 dcp 的变量都是这个 table)。此存储过程具有将数据插入 table discount_campaign 的功能。在 discount_campaign table 中有一列即 apply_all_product。如果 apply_all_product 在参数中填充 "0",那么另一个参数(别名 dcp)将在 [=19= 中插入一些数据]discount_campaign_产品table。但是当我用 apply_all_product = 0 申请时,这个存储过程只在 discount_campaign table 中插入数据, 不在 discount_campaign_product table.
我这个存储过程哪里出错了?
我猜您没有看到任何行插入到 discount_campaign_product
中,因为 product_categories
中没有匹配的行。您使用了 INSERT...SELECT FROM product_categories
但搜索的行 category_id IN (?)
.
我想你的意思是将其变成 category_id IN (1,2,3,4,5)
,但这不是查询参数在 SQL 中的工作方式。一个参数只能用于一个标量值。因此,在您的代码中,它将像您执行 category_id IN ('1,2,3,4,5')
一样进行搜索,即搜索单个 字符串 。它可能会匹配字符串中的第一个数字,因为它将字符串转换为数字,但如果没有与第一个数字匹配的类别,那么它将找不到匹配项。
解决方法是使用 FIND_IN_SET(category_id, '1,2,3,4,5')
。这实际上不是该函数的应有使用方式,但它可以在 comma-separated 整数列表中定位一个整数。注意不要在列表元素之间放置空格,因为它不适用于 FIND_IN_SET().
其他问题:
你 DECLARE
局部变量然后你不使用它们。明白在MySQL中,变量myvar
和@myvar
是两个不同的变量。您需要将没有 @
标志的类型声明为局部变量。而带有 @
印记的类型是 user-defined 会话变量,您无需声明——只需设置其值,变量就会被隐式创建。
您正在使用 PREPARE/EXECUTE,但在这种情况下不需要使用动态 SQL。您唯一的动态部分是值,因此您可以只使用过程的输入参数,就好像它们是查询参数一样。它们保证像单个标量一样工作,并且不会导致 SQL 注入。只有在查询中有动态表达式或动态标识符时才需要使用 PREPARE/EXECUTE,但实际上没有。
直接在查询中使用过程参数的唯一风险是您应确保参数名称与查询中表的任何列名称不同。
DECIMAL(10,0)
没有多大意义。为什么使用小数位数为 0 的小数位?那只是一个整数。
INT(11)
与 INT(50)
没有区别。 INT 类型的参数不会改变它们的大小或值的范围。事实上,在 MySQL 8.0 中已弃用对 INT 类型使用此参数,因此您应该养成删除它的习惯。
这是我测试过的程序,它有效:
CREATE PROCEDURE create_discount_campaign(
IN discount_campaign_discount_type_id CHAR(22),
IN discount_campaign_product_id INT,
IN discount_campaign_marketing_target_id INT,
IN discount_campaign_max_use_per_user INT,
IN discount_campaign_discount_code varchar(100),
IN discout_campaign_discount_amount decimal(10,0),
IN discount_campaign_start_date datetime,
IN discount_campaign_end_date datetime,
IN discount_campaign_min_order_quantity INT,
IN discount_campaign_min_order_price decimal(10,0),
IN discount_campaign_discount_quota INT,
IN discount_campaign_min_product_varian INT,
IN discount_campaign_apply_all_product INT,
IN discount_campaign_product_product_id INT UNSIGNED, -- this is unused?
IN discount_campaign_product_active TINYINT,
IN discount_campaign_product_createdby INT,
IN discount_campaign_product_updatedby INT,
IN discount_campaign_product_category_id VARCHAR(100),
IN discount_campaign_advanced_discount_advanced_rules_id INT,
IN discount_campaign_advanced_value varchar(255),
IN discount_campaign_advanced_status TINYINT
)
MODIFIES SQL DATA
BEGIN
DECLARE last_id BIGINT;
INSERT INTO discount_campaigns
SET
discount_type_id = discount_campaign_discount_type_id,
product_id = discount_campaign_product_id,
marketing_target_id = discount_campaign_marketing_target_id,
max_use_per_user = discount_campaign_max_use_per_user,
discount_code = discount_campaign_discount_code,
discount_amount = discout_campaign_discount_amount,
start_date = discount_campaign_start_date,
end_date = discount_campaign_end_date,
min_order_quantity = discount_campaign_min_order_quantity,
min_order_price = discount_campaign_min_order_price,
discount_quota = discount_campaign_discount_quota,
min_product_variant = discount_campaign_min_product_varian,
apply_all_products = discount_campaign_apply_all_product,
createdAt = NOW(),
updatedAt = NOW();
IF (discount_campaign_apply_all_product = 0) THEN
SET last_id = LAST_INSERT_ID();
INSERT INTO discount_campaign_product (discount_campaign_id,
product_id, active, createdby, updatedby, createdAt, updatedAt)
SELECT last_id, c.product_id, discount_campaign_product_active,
discount_campaign_product_createdby, NULL, NOW(), NULL
FROM product_categories AS c
WHERE FIND_IN_SET(c.category_id, discount_campaign_product_category_id)
AND c.status=1;
END IF;
END
请注意,我对第一个 INSERT 使用了不同的语法。这是对标准 SQL 的 MySQL 扩展,它仅适用于 single-row 插入。但它非常方便,因为它使它变得更容易。在带有列列表然后是 VALUES 子句的标准 INSERT 语法中,我发现很难确保我已将列与正确的值匹配。 INSERT ... SET
语法更具可读性。
我有一个大约 2 table 秒的存储过程,如下所示:
CREATE DEFINER=`brambang`@`%` PROCEDURE `create_discount_campaign`(
IN discount_campaign_discount_type_id CHAR(22),
IN discount_campaign_product_id INT(10),
IN discount_campaign_marketing_target_id INT(10),
IN discount_campaign_max_use_per_user int(11),
IN discount_campaign_discount_code varchar(100),
IN discout_campaign_discount_amount decimal(10,0),
IN discount_campaign_start_date datetime,
IN discount_campaign_end_date datetime,
IN discount_campaign_min_order_quantity INT(11),
IN discount_campaign_min_order_price decimal(10,0),
IN discount_campaign_discount_quota INT(11),
IN discount_campaign_min_product_varian INT(11),
IN discount_campaign_apply_all_product INT(10),
IN discount_campaign_product_product_id int(10) unsigned,
IN discount_campaign_product_active tinyint(1),
IN discount_campaign_product_createdby int(11),
IN discount_campaign_product_updatedby int(11),
IN discount_campaign_product_category_id VARCHAR(100),
IN discount_campaign_advanced_discount_advanced_rules_id int(50),
IN discount_campaign_advanced_value varchar(255),
IN discount_campaign_advanced_status tinyint(4)
)
proc:
BEGIN
DECLARE is_dc_discount_type_id INT(10);
DECLARE is_dc_product_id INT(10);
DECLARE is_dc_marketing_target_id INT(10);
DECLARE is_dc_max_use_per_user INT(11);
DECLARE is_dc_discount_code varchar(100);
DECLARE is_dc_discount_amount decimal(10,0);
DECLARE is_dc_start_date datetime;
DECLARE is_dc_end_date datetime;
DECLARE is_dc_min_order_quantity INT(11);
DECLARE is_dc_min_order_price decimal(10,0);
DECLARE is_dc_discount_quota INT(11);
DECLARE is_dc_min_product_variant INT(11);
DECLARE is_dc_apply_all_product INT(10);
DECLARE is_dcp_product_id INT(10);
DECLARE is_dcp_active tinyint(1);
DECLARE is_dcp_createdby int(11);
DECLARE is_dcp_updatedby int(11);
DECLARE is_dca_discount_advanced_rules_id int(50);
DECLARE is_dca_value varchar(255);
DECLARE is_dca_status tinyint(4);
DECLARE LAST_INSERT_ID int(11);
DECLARE discount_campaign_product_category_id varchar(100);
SET @querie = NULL;
SET @querie2 = NULL;
SET @querie3 = NULL;
SET lc_time_names = "id_ID";
SET @is_dc_discount_type_id = NULL;
SET @is_dc_product_id = NULL;
SET @is_dc_marketing_target_id = NULL;
SET @is_dc_max_use_per_user = NULL;
SET @is_dc_discount_code = NULL;
SET @is_dc_discount_amount = NULL;
SET @is_dc_start_date = NULL;
SET @is_dc_end_date = NULL;
SET @is_dc_min_order_quantity = NULL;
SET @is_dc_min_order_price = NULL;
SET @is_dc_discount_quota = NULL;
SET @is_dc_min_product_variant = NULL;
SET @is_dc_apply_all_product = NULL;
SET @is_dcp_product_id = NULL;
SET @is_dcp_active = NULL;
SET @is_dcp_createdby = NULL;
SET @is_dcp_updatedby = NULL;
SET @is_dca_discount_advanced_rules_id = NULL;
SET @is_dca_value = NULL;
SET @is_dca_status = NULL;
SET @LAST_INSERT_ID = NULL;
SET @discount_campaign_product_category_id = NULL;
SET @is_dc_discount_type_id = discount_campaign_discount_type_id;
SET @is_dc_product_id = discount_campaign_product_id;
SET @is_dc_marketing_target_id = discount_campaign_marketing_target_id;
SET @is_dc_max_use_per_user = discount_campaign_max_use_per_user;
SET @is_dc_discount_code = discount_campaign_discount_code;
SET @is_dc_discount_amount = discout_campaign_discount_amount;
SET @is_dc_start_date = discount_campaign_start_date;
SET @is_dc_end_date = discount_campaign_end_date;
SET @is_dc_min_order_quantity = discount_campaign_min_order_quantity;
SET @is_dc_min_order_price = discount_campaign_min_order_price;
SET @is_dc_discount_quota = discount_campaign_discount_quota;
SET @is_dc_min_product_variant = discount_campaign_min_product_varian;
SET @is_dc_apply_all_product = discount_campaign_apply_all_product;
SET @discount_campaign_product_category_id = discount_campaign_product_category_id;
SET @querie = CONCAT("
INSERT INTO discount_campaigns (
discount_type_id,
product_id,
marketing_target_id,
max_use_per_user,
discount_code,
discount_amount,
start_date,
end_date,
min_order_quantity,
min_order_price,
discount_quota,
min_product_variant,
apply_all_products,
createdAt,
updatedAt
) VALUES (
?,
?,
?,
?,
?,
?,
?,
?,
?,
?,
?,
?,
?,
NOW(),
NOW()
);
");
PREPARE stmt FROM @querie;
EXECUTE stmt
USING
@is_dc_discount_type_id,
@is_dc_product_id,
@is_dc_marketing_target_id,
@is_dc_max_use_per_user,
@is_dc_discount_code,
@is_dc_discount_amount,
@is_dc_start_date,
@is_dc_end_date,
@is_dc_min_order_quantity,
@is_dc_min_order_price,
@is_dc_discount_quota,
@is_dc_min_product_variant,
@is_dc_apply_all_product;
DEALLOCATE PREPARE stmt;
SET @is_dcp_product_id = discount_campaign_product_product_id;
SET @is_dcp_active = discount_campaign_product_active;
SET @is_dcp_createdby = discount_campaign_product_createdby;
SET @is_dcp_updatedby = discount_campaign_product_updatedby;
SET @LAST_INSERT_ID = LAST_INSERT_ID();
IF (discount_campaign_apply_all_product = 0) THEN
SET @querie2 = CONCAT("INSERT INTO discount_campaign_product (discount_campaign_id,
product_id,
active,
createdby,
updatedby,
createdAt,
updatedAt
) SELECT ?,product_id,?,?,NULL,NOW(),NULL
FROM product_categories WHERE category_id IN
(?) AND status=1");
PREPARE stmt_2 FROM @querie2;
EXECUTE stmt_2
USING
@LAST_INSERT_ID,
@is_dcp_active,
@is_dcp_createdby,
@discount_campaign_product_category_id;
DEALLOCATE PREPARE stmt_2;
END IF;
END
基本上我有 2 tables discount_campaign(任何带有别名 dc 的变量都是 table) 和 discount_campaign_product(任何带有别名 dcp 的变量都是这个 table)。此存储过程具有将数据插入 table discount_campaign 的功能。在 discount_campaign table 中有一列即 apply_all_product。如果 apply_all_product 在参数中填充 "0",那么另一个参数(别名 dcp)将在 [=19= 中插入一些数据]discount_campaign_产品table。但是当我用 apply_all_product = 0 申请时,这个存储过程只在 discount_campaign table 中插入数据, 不在 discount_campaign_product table.
我这个存储过程哪里出错了?
我猜您没有看到任何行插入到 discount_campaign_product
中,因为 product_categories
中没有匹配的行。您使用了 INSERT...SELECT FROM product_categories
但搜索的行 category_id IN (?)
.
我想你的意思是将其变成 category_id IN (1,2,3,4,5)
,但这不是查询参数在 SQL 中的工作方式。一个参数只能用于一个标量值。因此,在您的代码中,它将像您执行 category_id IN ('1,2,3,4,5')
一样进行搜索,即搜索单个 字符串 。它可能会匹配字符串中的第一个数字,因为它将字符串转换为数字,但如果没有与第一个数字匹配的类别,那么它将找不到匹配项。
解决方法是使用 FIND_IN_SET(category_id, '1,2,3,4,5')
。这实际上不是该函数的应有使用方式,但它可以在 comma-separated 整数列表中定位一个整数。注意不要在列表元素之间放置空格,因为它不适用于 FIND_IN_SET().
其他问题:
你 DECLARE
局部变量然后你不使用它们。明白在MySQL中,变量myvar
和@myvar
是两个不同的变量。您需要将没有 @
标志的类型声明为局部变量。而带有 @
印记的类型是 user-defined 会话变量,您无需声明——只需设置其值,变量就会被隐式创建。
您正在使用 PREPARE/EXECUTE,但在这种情况下不需要使用动态 SQL。您唯一的动态部分是值,因此您可以只使用过程的输入参数,就好像它们是查询参数一样。它们保证像单个标量一样工作,并且不会导致 SQL 注入。只有在查询中有动态表达式或动态标识符时才需要使用 PREPARE/EXECUTE,但实际上没有。
直接在查询中使用过程参数的唯一风险是您应确保参数名称与查询中表的任何列名称不同。
DECIMAL(10,0)
没有多大意义。为什么使用小数位数为 0 的小数位?那只是一个整数。
INT(11)
与 INT(50)
没有区别。 INT 类型的参数不会改变它们的大小或值的范围。事实上,在 MySQL 8.0 中已弃用对 INT 类型使用此参数,因此您应该养成删除它的习惯。
这是我测试过的程序,它有效:
CREATE PROCEDURE create_discount_campaign(
IN discount_campaign_discount_type_id CHAR(22),
IN discount_campaign_product_id INT,
IN discount_campaign_marketing_target_id INT,
IN discount_campaign_max_use_per_user INT,
IN discount_campaign_discount_code varchar(100),
IN discout_campaign_discount_amount decimal(10,0),
IN discount_campaign_start_date datetime,
IN discount_campaign_end_date datetime,
IN discount_campaign_min_order_quantity INT,
IN discount_campaign_min_order_price decimal(10,0),
IN discount_campaign_discount_quota INT,
IN discount_campaign_min_product_varian INT,
IN discount_campaign_apply_all_product INT,
IN discount_campaign_product_product_id INT UNSIGNED, -- this is unused?
IN discount_campaign_product_active TINYINT,
IN discount_campaign_product_createdby INT,
IN discount_campaign_product_updatedby INT,
IN discount_campaign_product_category_id VARCHAR(100),
IN discount_campaign_advanced_discount_advanced_rules_id INT,
IN discount_campaign_advanced_value varchar(255),
IN discount_campaign_advanced_status TINYINT
)
MODIFIES SQL DATA
BEGIN
DECLARE last_id BIGINT;
INSERT INTO discount_campaigns
SET
discount_type_id = discount_campaign_discount_type_id,
product_id = discount_campaign_product_id,
marketing_target_id = discount_campaign_marketing_target_id,
max_use_per_user = discount_campaign_max_use_per_user,
discount_code = discount_campaign_discount_code,
discount_amount = discout_campaign_discount_amount,
start_date = discount_campaign_start_date,
end_date = discount_campaign_end_date,
min_order_quantity = discount_campaign_min_order_quantity,
min_order_price = discount_campaign_min_order_price,
discount_quota = discount_campaign_discount_quota,
min_product_variant = discount_campaign_min_product_varian,
apply_all_products = discount_campaign_apply_all_product,
createdAt = NOW(),
updatedAt = NOW();
IF (discount_campaign_apply_all_product = 0) THEN
SET last_id = LAST_INSERT_ID();
INSERT INTO discount_campaign_product (discount_campaign_id,
product_id, active, createdby, updatedby, createdAt, updatedAt)
SELECT last_id, c.product_id, discount_campaign_product_active,
discount_campaign_product_createdby, NULL, NOW(), NULL
FROM product_categories AS c
WHERE FIND_IN_SET(c.category_id, discount_campaign_product_category_id)
AND c.status=1;
END IF;
END
请注意,我对第一个 INSERT 使用了不同的语法。这是对标准 SQL 的 MySQL 扩展,它仅适用于 single-row 插入。但它非常方便,因为它使它变得更容易。在带有列列表然后是 VALUES 子句的标准 INSERT 语法中,我发现很难确保我已将列与正确的值匹配。 INSERT ... SET
语法更具可读性。