MySQL 重复列表中的过程

MySQL REPEAT procedure from a list

NOTICE: 在这个问题中我只对一般情况感兴趣,但是为了简单起见,我会考虑一个特定的情况。

假设我想创建一个 table,其中的列来自列表。

所以,我想从列表中批量创建 tables:

SET @list = "foo,bar,baz";
SET @query = '';

SET @query = 'CREATE TABLE foobar (';
-- FOREACH @list ASs name
SET @query = CONCAT(@query, name, ' INT,')
-- ENDFOREACH

SET @query = CONCAT(@query, ')'); -- Yes I need to remove the extra semicolon

PREPARE stm1;
FROM @query;
EXECUTE stm1;
DEALLOCATE PREPARE stm1;

很遗憾,我还没有找到重复的方法。我当前的解决方案是使用我可以使用的所有名称创建一个虚拟 table:

CREATE TABLE dummy (name VARCHAR(100));
INSERT INTO dummy (`name`) VALUES ('foo'), ('bar'), ('baz');

那我可以做:

SELECT name FROM dummy WHERE FIND_IN_SET(name, @list);

所以我可以做到:

SELECT 
    @query := CONCAT(@query, ' ', 
   '...'
) 
FROM dummy 
WHERE FIND_IN_SET(name, @list);

我有办法简化这个吗?

一个解决方案是使用这个技巧:

SET @query = '';
delimiter |
CREATE PROCEDURE foo(IN names VARCHAR(100))
BEGIN
SET @myArrayOfValue = CONCAT(names, ',');
    WHILE (LOCATE(',', @myArrayOfValue) > 0)
    DO
        SET @value = LEFT(@myArrayOfValue, LOCATE(',',@myArrayOfValue) - 1);    
        SET @myArrayOfValue = SUBSTRING(@myArrayOfValue, LOCATE(',',@myArrayOfValue) + 1);

        -- BEGIN The code you want to repeat
        SET @query = CONCAT(@query, ' CREATE TABLE ', @value, ' ( id INT AUTO_INCREMENT PRIMARY KEY);');
        -- END
    END WHILE;
END
|

然后你可以简单地调用你的程序:

CALL foo('foo,bar,baz');

您不能使用 PREPARE 准备多个以分号分隔的查询。你必须一次做一个。

mysql> set @sql = "select * from foo ; select * from bar";
Query OK, 0 rows affected (0.01 sec)

mysql> prepare stmt from @sql;
ERROR 1064 (42000): 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 'select * from bar' at line 1

我不知道为什么这么多开发人员想在 MySQL 中使用存储过程。他们很可怕。没有包,没有用户定义的数据类型,没有编译器,没有调试器,文档也很差。

您描述的任务用任何脚本语言都更容易实现。选择您最喜欢的语言,其中任何一种都可以胜任这项任务。

这是 Ruby 中的示例:

require 'Mysql2'
db = Mysql2::Client.new(:host => "localhost", :username => "root", :database => "test")

["foo", "bar", "baz"].each do |table|
  db.query("CREATE TABLE `#{table}` ( id INT AUTO_INCREMENT PRIMARY KEY )")
end

将此与使用存储过程时必需的棘手的字符串拆分和连接、准备和执​​行进行比较。

我不使用 MySQL 存储过程,我不建议其他人使用它们,例外情况除外。我使用存储过程的一个案例是授予用户 运行 一些 SQL 语句针对他们无权查看的数据的能力。


这是一个解决方案,已在 MySQL 5.6 上测试:

DELIMITER ;;
CREATE PROCEDURE the_worst_solution_for_this_task(IN tablenames TEXT)
BEGIN
 DECLARE i INT;
 DECLARE tablename VARCHAR(64);
 SELECT LENGTH(tablenames)-LENGTH(REPLACE(tablenames, ',', ''))+1 INTO i;
 WHILE i > 0
 DO
  SELECT TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(tablenames, ',', i), ',', -1)) INTO tablename;
  SET @sql = CONCAT('CREATE TABLE `', tablename, '` (id INT AUTO_INCREMENT PRIMARY KEY)');
  PREPARE stmt FROM @sql;
  EXECUTE stmt;
  DEALLOCATE PREPARE stmt;
  SET i = i - 1;
 END WHILE;
END;;
DELIMITER ;

CALL the_worst_solution_for_this_task('foo,bar,baz');

SHOW TABLES;
+----------------+
| Tables_in_test |
+----------------+
| bar            |
| baz            |
| foo            |
+----------------+