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 |
+----------------+
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 |
+----------------+