我已经创建了一个存储过程,以返回作为“ 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。
我测试存储过程的方法是否错误,还是应该归咎于存储过程?
答案 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
的值连接在一起。由于rowcount
是out
参数,因此它现在为空。如果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,'`','``'),'`');