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

时间:2018-07-02 20:54:25

标签: mysql

我想创建一个将创建唯一的随机ID的函数。参数将简单地为min(最小数),max(最大数)和tablename(表名,以检查{ {1}}函数已经存在。

通过其他文章,我发现您不能将表名传递给函数,因为函数不能执行动态SQL,但是您可以将它们传递给存储过程。我在StackOverflow上发现了很多有关如何将表名传递到存储过程中的示例,它们都归结为使用准备好的语句。

我已经创建了一个存储过程,如下所示:

rand()

每当我运行以下代码:

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;

我不知所措。我在某个地方看到您不能在存储过程中使用用户变量,但这似乎是不正确的,因为在StackOverflow上有很多示例,其中正确的解决方案正是这样做的。

很抱歉,我对MySQL的了解不高。我确定我的代码充满语法错误和不良的设计。感谢您能提供的任何帮助。我研究了很长时间,尝试了许多尝试,但无济于事。上面的代码部分是我所能获得的最接近的代码,并且产生的错误最少,但仍然无法正常工作。

谢谢。

编辑:根据@Barmar回答中的第二个示例,我将代码更改为如下形式:

#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

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

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

编辑:这是我的代码更改为适合@slaakso的答案,并添加了@Barmar关于使用@count_id的内容:

#1327 - Undeclared variable: count_id

2 个答案:

答案 0 :(得分:1)

在分配@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使用用户变量。

答案 1 :(得分:0)

首先,使用随机数作为表的ID是非常不寻常的。 Mabye,您应该考虑使用AUTO_INCREMENT列。

如果您真的想使用随机数,请修复以下代码:

  1. 您第一次运行查询时应使用uid的值 (如果没有它,将为NULL,因此会出现错误)。
  2. 动态查询中缺少SELECT
  3. INTO count_id”语法不起作用,因为count_id在动态SQL中不可见(改为使用@var变量)
  4. 您的最小值和最大值被声明为INT,但您传递的参数超出了INT范围(-2147483648-2147483647)