【问题标题】:OUT parameter of MySQL stored procedure is null after calling stored procedureMySQL存储过程调用存储过程后OUT参数为空
【发布时间】:2020-01-09 17:42:52
【问题描述】:

我创建了一个存储过程来返回作为“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。

是我测试存储过程的方法不正确,还是应该归咎于存储过程?

【问题讨论】:

  • 你正在从 sql_text1 做准备...那么 sql_text2 和 sql_final 什么时候执行?

标签: mysql stored-procedures mysql-workbench


【解决方案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 注入的安全漏洞,恶意输入可能会导致意外/未经授权的结果。即使可以论证该输入来自可信来源,该论证仍被视为最佳实践问题,因为未来的变化可能会使该假设无效。值得您花时间学习这样做,并始终如一地这样做,这样它就会成为您的第二天性。

您可以安全地转义 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,'`','``'),'`');

【讨论】:

    【解决方案2】:

    使用这个存储过程:

    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将计数存储到会话变量@rowcount中,该变量将自动创建。剩下的就是简单地将结果传递给适当的变量。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-01-01
      • 1970-01-01
      • 2013-10-10
      • 1970-01-01
      • 1970-01-01
      • 2012-11-21
      • 1970-01-01
      相关资源
      最近更新 更多