2016-04-14 5 views
2

Я пытаюсь написать сценарий MySQL, отбрасывая некоторые таблицы, выбранные по шаблону, но моя процедура не компилируется. Кто-нибудь может посоветовать, что с ним не так, пожалуйста?Выполнение динамических запросов mysql

delimiter # 
drop procedure if exists drop_audit_tables # 
create procedure drop_audit_tables() 
begin 
    declare done int default false; 
    declare cmd varchar(4000); 
    declare cmds cursor for select 'drop table [' + table_name + ']' from information_schema.tables where table_name like '%_audit'; 
    declare continue handler for not found set done = true; 
    open cmds; 
    tLoop: loop 
    fetch cmds into cmd; 
    if done then 
     leave tLoop; 
    end if; 
    PREPARE STMT FROM cmd; 
    EXECUTE STMT; 
    DEALLOCATE PREPARE STMT; 
    end loop tLoop; 
    close cmds; 
end # 

сообщение об ошибке:

[42000] [1064] У вас есть ошибка в вашем SQL синтаксиса; проверьте руководство, соответствующее версии вашего сервера MySQL, для правильного синтаксиса для использования рядом с 'cmd; EXECUTE STMT; DEALLOCATE PREPARE STMT; end loop tLoop; близко см»в строке 13

+0

Я никогда не использовал курсоры, но я могу сказать, что 'ГОТОВИТ STMT FROM ЦМДА,' может потерпеть неудачу, если 'cmd' не является строка, которая представляет собой действующую команду SQL. Я вижу ваши 'fetch cmds в cmd;' строку, но это действительно хорошая идея? Эта строка фактически помещает команду SQL в 'cmd', или * result set * третьего выражения' declare'. Просто пытаюсь помочь. Энтони. –

+0

Я могу подождать, чтобы изменить разделитель после запуска 'drop procedure, если существует drop_audit_tables'. –

+0

Где находится '['+ table_name +'] ''? Откуда появляется табличное имя? –

ответ

0

Ваша линия:

declare cmds cursor for select 'drop table [' + table_name + ']' from information_schema.tables where table_name like '%_audit'; 

.. использует table_name без определения его первым.

Попробуйте определяя его первый что-то вроде:

create procedure drop_audit_tables(IN table_name VARCHAR(64)) 

Вы можете захотеть рассмотреть последствия для безопасности с переменной непосредственно из хранимой процедуры и размещения его в одноранговой запросе. Еще, определите table_name где-нибудь. В этом случае table_name будет предоставлен в качестве аргумента вашей хранимой процедуре. Затем ваша задача - собрать массив имен таблиц и запустить этот код в цикле for/foreach.

Basic (не прочный) PHP (PDO)

/* Get the audit tables. */ 
$stmt = $pdo->query(`CALL get_audit_tables()`) 
$tables = $stmt->fetch(); 
$stmt->close() 

$stmt = $pdo->prepare('CALL drop_audit_tables(:table)') 

/* Drop each audit table. */ 
foreach($tables as $table) 
{ 
    $stmt->bindParam(:table, $table, PDO::PARAM_STR) 
    $stmt->execute(); 
} 

$stmt->close(); 

Что-то вроде этого, в любом случае.

MySQL: CREATE PROCEDURE

Указание параметра, как IN, OUT или INOUT действительна только в течение процедуры. Для функции FUNCTION параметры всегда считаются параметрами IN.

PHP Manual: PDO::prepare

Готовит инструкцию SQL, которая будет выполнена по PDOStatement :: Execute() метод. Оператор SQL может содержать ноль или более имени (: name) или маркеров параметров вопросительного знака (?), Для которых действительные значения будут заменены при выполнении оператора.

Решение, подобное этому, облегчит вам жизнь. Вам нужно всего лишь определить базовый запрос, который найдет ваши таблицы аудита. Меньше кода. Simpler.

0

Вы можете избежать курсора:

mysql> DROP TABLE IF EXISTS `one_audit`; 
Query OK, 0 rows affected (0.00 sec) 

mysql> DROP TABLE IF EXISTS `two_audit`; 
Query OK, 0 rows affected (0.01 sec) 

mysql> DROP TABLE IF EXISTS `three_audit`; 
Query OK, 0 rows affected (0.00 sec) 

mysql> CREATE TABLE `one_audit`(`a` INT); 
Query OK, 0 rows affected (0.00 sec) 

mysql> CREATE TABLE `two_audit`(`a` INT); 
Query OK, 0 rows affected (0.00 sec) 

mysql> CREATE TABLE `three_audit`(`a` INT); 
Query OK, 0 rows affected (0.00 sec) 

mysql> SET @`drop_tables` := (
    -> SELECT 
    ->  CONCAT('DROP TABLE IF EXISTS ', 
    ->  GROUP_CONCAT(CONCAT('`', `TABLE_NAME`, '`') SEPARATOR ', ')) 
    -> FROM 
    ->  `information_schema`.`TABLES` 
    -> WHERE 
    ->  `TABLE_SCHEMA` = DATABASE() AND 
    ->  `TABLE_TYPE` = 'BASE TABLE' AND 
    ->  `TABLE_NAME` LIKE '%_audit' 
    ->); 
Query OK, 0 rows affected (0.00 sec) 

mysql> SELECT @`drop_tables`; 
+--------------------------------------------------------------+ 
| @`drop_tables`            | 
+--------------------------------------------------------------+ 
| DROP TABLE IF EXISTS `one_audit`, `three_audit`, `two_audit` | 
+--------------------------------------------------------------+ 
1 row in set (0.00 sec) 

mysql> PREPARE `exec` FROM @`drop_tables`; 
Query OK, 0 rows affected (0.00 sec) 
Statement prepared 

mysql> EXECUTE `exec`; 
Query OK, 0 rows affected (0.00 sec) 

mysql> DEALLOCATE PREPARE `exec`; 
Query OK, 0 rows affected (0.00 sec) 

Вы должны быть осторожны с системной переменной group_concat_max_len.

ОБНОВЛЕНИЕ

С помощью курсора:

DELIMITER # 

DROP PROCEDURE IF EXISTS `drop_audit_tables`# 

CREATE PROCEDURE `drop_audit_tables`() 
BEGIN 
    DECLARE `done` BOOL DEFAULT 0; 
    DECLARE `cmd` VARCHAR(4000); 
    DECLARE `cmds` CURSOR FOR 
    SELECT 
    CONCAT('DROP TABLE IF EXISTS `', `TABLE_NAME`, '`') 
    FROM 
    `information_schema`.`TABLES` 
    WHERE 
    `TABLE_SCHEMA` = DATABASE() AND 
    `TABLE_TYPE` = 'BASE TABLE' AND 
    `TABLE_NAME` LIKE '%_audit'; 
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET `done` := 1; 

    OPEN `cmds`; 

    `tLoop`: LOOP 
    FETCH `cmds` INTO `cmd`; 
    IF `done` THEN 
     CLOSE `cmds`; 
     LEAVE `tLoop`; 
    END IF; 
    SET @`cmd` := `cmd`; 
    PREPARE `STMT` FROM @`cmd`; 
    EXECUTE `STMT`; 
    DEALLOCATE PREPARE `STMT`; 
    END LOOP `tLoop`; 

    SET @`cmd` := NULL; 
END# 

CALL `drop_audit_tables`# 

DELIMITER ; 

14.5.1 PREPARE Syntax

ГОТОВИШЬ stmt_name ОТ preparable_stmt

...

preparable_stmt либо строковый или переменный пользователя, содержит текст инструкции SQL.

...

+0

Я бы спросил только, откуда приходит первая ссылка 'TABLE_NAME'', которая позволяет ее конкатенировать. –

+0

@AnthonyRutledge: столбец 'TABLE_NAME' из таблицы' TABLES'. 'SELECT \' TABLE_NAME \ 'FROM \' information_schema \ '. \' TABLES \ ';'. – wchiquito

+0

Не могли бы вы рассказать? –

Смежные вопросы