mysql—存储过程中的动态表名变量

fkaflof6  于 2021-06-20  发布在  Mysql
关注(0)|答案(2)|浏览(401)

我想创建一个函数来创建一个唯一的随机id min (最小数量), max (最大数量),以及 tablename (要检查的表的名称,以查看 rand() 函数已存在)。
我在其他文章中发现,不能将表名传递到函数中,因为函数不能执行动态sql,但可以将它们传递到存储过程中。我在stackoverflow上找到了许多例子,说明如何将表名传递到存储过程中,它们都归结为使用准备好的语句。
我创建了一个存储过程,如下所示:

DELIMITER $$
CREATE DEFINER=`user`@`localhost` PROCEDURE `rand_id`(IN `min` INT, IN `max` INT, IN `tablename` VARCHAR(20) CHARSET utf8, OUT `uid` INT)
BEGIN 
  DECLARE count_id int;
  SET count_id = 1;
  SET @s = CONCAT('COUNT(`id`) INTO count_id FROM `', tablename, '` WHERE `id` = ', uid);
  WHILE count_id > 0 DO
    SET uid = FLOOR(rand() * max + min);
    PREPARE stmt from @s;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
  END WHILE;
END$$
DELIMITER ;

每当我运行以下代码时:

CALL rand_id(1000000000, 9999999999, 'test', @id);
SELECT @id;

我得到这个错误:


# 1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'NULL' at line 1

我不知道发生了什么事。我在某个地方看到不能在存储过程中使用用户变量,但这似乎是不正确的,因为在stackoverflow上有很多例子,正确的解决方案就是这样做的。
对不起,我对mysql的理解水平很低。我确信我的代码充满了语法错误和糟糕的设计。我很感激能得到的任何帮助。我研究了很长一段时间,尝试了很多东西,但都没有结果。上面的代码部分是我所能得到的最接近的部分,并且产生的错误最少,但是它仍然不起作用。
谢谢您。
编辑:根据@barmar答案中的第二个示例,我将代码更改为:

BEGIN 
  DECLARE count_id int;
  SET count_id = 1;
  SET @s = CONCAT('SELECT COUNT(`id`) INTO count_id FROM `', tablename, '` WHERE `id` = ?');
  PREPARE stmt from @s;
  WHILE count_id > 0 DO
    SET @uid = FLOOR(rand() * max + min);
    EXECUTE stmt USING @uid;
  END WHILE;
  DEALLOCATE PREPARE stmt;
  SET uid = @uid;
END

它似乎已经解决了我最初的问题,但现在我得到了这个错误:


# 1327 - Undeclared variable: count_id

编辑:这是我修改的代码,以符合@slaakso的答案,并添加@barmar所说的关于使用@count\u id:

DELIMITER $$
CREATE DEFINER=`mjrinker`@`localhost` PROCEDURE `rand_id`(IN `min` BIGINT, IN `max` BIGINT, IN `tablename` VARCHAR(128) CHARSET utf8, OUT `uid` BIGINT)
BEGIN 
  SET @count_id = 1;
  SET uid = 0;
  SET @s = CONCAT('SELECT COUNT(`id`) INTO @count_id FROM `', tablename, '` WHERE `id` = ?');
  PREPARE stmt from @s;
  WHILE @count_id > 0 DO
    SET @uid = FLOOR(rand() * max + min);
    EXECUTE stmt USING @uid;
  END WHILE;
  DEALLOCATE PREPARE stmt;
  SET uid = @uid;
END$$
DELIMITER ;
mf98qq94

mf98qq941#

你需要分配 @s 在分配 uid 变量。
你也错过了 SELECT 查询中的关键字。

SET @count_id = 1
  WHILE @count_id > 0 DO
    SET uid = FLOOR(rand() * max + min);
    SET @s = CONCAT('SELECT COUNT(`id`) INTO @count_id FROM `', tablename, '` WHERE `id` = ', uid);
    PREPARE stmt from @s;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
  END WHILE;

但实际上,您应该只准备一次语句,使用占位符,在使用 EXECUTE .

SET @count_id = 1
  SET @s = CONCAT('SELECT COUNT(`id`) INTO @count_id FROM `', tablename, '` WHERE `id` = ?');
  PREPARE stmt from @s;
  WHILE @count_id > 0 DO
    SET @uid = FLOOR(rand() * max + min);
    EXECUTE stmt USING @uid;
  END WHILE;
  DEALLOCATE PREPARE stmt;
  SET uid = @uid;

请注意,参数 EXECUTE 必须是用户变量,这就是我改变的原因 uid@uid 在那里。然后在循环的末尾设置输出参数。
您还需要使用用户变量 INTO @count_id .

iaqfqrcu

iaqfqrcu2#

首先,使用随机数作为表的id是非常不寻常的。也许你应该考虑使用自动递增列。
如果您真的想使用随机数,请修复以下代码:
您应该使用值 uid 第一次运行查询时(如果没有它,它将为空,因此返回错误)。
你失踪了 SELECT 在动态查询中
“这个” INTO count_id “由于count\u id在动态sql中不可见(请使用 @var 变量)
最小值和最大值声明为int,但传递的参数超出int范围(-2147483648-2147483647)

相关问题