调用存储过程后,MySQL存储过程的OUT参数为null

时间:2019-09-06 21:50:44

标签: mysql stored-procedures mysql-workbench

我已经创建了一个存储过程,以返回作为“ IN”参数传入的任何表的行计数值,并将该行数输出到OUT参数

PROCEDURE `GetCount`(in tblname varchar(255), out rowcount int)
BEGIN
    SET @sql_text1 = concat('SELECT COUNT(*) FROM ',tblname);
    SET @sql_text2 = concat(@sql_text1,' INTO ');
    SET @sql_final = concat(@sql_text2,  rowcount);

    PREPARE stmt1 FROM @sql_text1;
    EXECUTE stmt1;
    DEALLOCATE PREPARE stmt1;

END

当我在MySQL工作台中打开查询窗口并执行以下操作时:

set @tablename = 'my_table_name;
set @cnt = -9998;
call GetCount(@tablename,@cnt);
SELECT @cnt;

@cnt的值为NULL。

我测试存储过程的方法是否错误,还是应该归咎于存储过程?

2 个答案:

答案 0 :(得分:1)

使用此存储的过程

DELIMITER //
DROP PROCEDURE IF EXISTS GetCount //
CREATE DEFINER=`root`@`localhost` PROCEDURE `GetCount`(IN tblname varchar(255), OUT rowcount int)
BEGIN

  SET @sql_text1 = concat('SELECT COUNT(*) FROM ',tblname);
  SET @sql_text1 = concat(@sql_text1,' INTO ');
  SET @sql_text1 = concat(@sql_text1, ' @rowcount;' );

  PREPARE stmt1 FROM @sql_text1;
  EXECUTE stmt1;
  Set rowcount = @rowcount;
  DEALLOCATE PREPARE stmt1;

END
//
DELIMITER ;

这个想法是mysql将计数存储到将自动创建的sessionvariable @rowcount中。其余的很简单,即可将结果转换为适当的变量。

答案 1 :(得分:1)

您的测试方法是有效的,但是您在该过程中犯了三个错误。

错误#1您为准备好的语句使用了错误的变量。

PREPARE stmt1 FROM @sql_text1;

这应该是...

PREPARE stmt1 FROM @sql_final;

错误2:这与您的预期目标不符:

SET @sql_final = concat(@sql_text2,  rowcount);

这将@sql_text2的值与 rowcount的值连接在一起。由于rowcountout参数,因此它现在为空。如果CONCAT()的任何参数为null,则结果也为null,因此实际上是将@sql_final设置为null。如果不是错误#1,则PREPARE或后续的EXECUTE都将引发有关...the right syntax to use near NULL at line 1的错误。

这就是您真正想要的,原义字符串'rowcount':

SET @sql_final = concat(@sql_text2,  'rowcount');

...但是那也会失败,因为rowcount是一个程序变量。准备好的语句在会话范围而不是程序范围内运行,因此程序变量不在准备好的语句中。

此修复程序要求您使用具有会话范围的用户定义变量,然后将其复制到程序变量中,如其答案中的@nbk所示。

SET @sql_final = concat(@sql_text2,  '@rowcount');
PREPARE stmt1 FROM @sql_text1;
EXECUTE stmt1;
SET rowcount = @rowcount;

请注意,rowcount之类的程序变量和@rowcount之类的用户定义变量来自完全不同的名称空间,因此,名称不必相同,名称也不必不同

错误3并不是严格意义上的错误,从某种意义上讲它并不会阻止您的代码正常工作,但这是有关潜在危险做法的说明。

您接受一个表名作为输入,这将打开一个名为SQL Injection的安全漏洞,在该漏洞中,恶意输入可能导致意外/未经授权的结果。即使可以说此输入来自受信任的来源,也不能将其视为最佳实践,因为将来的更改可能会使该假设无效。值得您花时间学习如何做到这一点,并且始终如一地做到这一点,以使其成为您的第二天性。

您可以在MySQL中安全地对表名,列名或其他对象标识符进行转义,方法是将所有嵌入的反引号替换为双反引号,然后在每个末端添加一个反引号。

您可以在过程的顶部执行此操作...

SET tblname = CONCAT('`',REPLACE(tblname,'`','``'),'`');

...或内联...

SET @sql_text1 = concat('SELECT COUNT(*) FROM ',CONCAT('`',REPLACE(tblname,'`','``'),'`'));

...但是当然不能两者兼而有之。在第二个示例中,嵌套CONCAT()并不是严格必需的,因此它也可以工作,但意图并不明显:

SET @sql_text1 = concat('SELECT COUNT(*) FROM ','`',REPLACE(tblname,'`','``'),'`');