MySQL REPEAT процедура из списка - PullRequest
0 голосов
/ 23 марта 2019

УВЕДОМЛЕНИЕ : В этом вопросе меня интересует только общий случай, но для простоты я бы рассмотрел конкретный случай.

Давайте представим, что я хотел бы создать таблицу со столбцами, взятыми из списка.

Итак, я бы хотел создать пакетные таблицы из списка:

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;

К сожалению, я не нашел, как сделать повторение. Мое текущее решение - создать фиктивную таблицу со всеми именами, которые я мог бы использовать:

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);

Есть ли способ упростить это?

Ответы [ 2 ]

0 голосов
/ 23 марта 2019

Нельзя использовать 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            |
+----------------+
0 голосов
/ 23 марта 2019

Одним из решений будет использование этого трюка:

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');
...