2016-04-14 23 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」の近くで使用できるようにしてください。実行STMT; DEALLOCATE PREPARE STMT;エンドループtLoop;ラインで近いCM」13

+0

私は、カーソルを使用したことがないが、私は 'CMD FROM STMTを準備すると言うことができます;' cmd'は、有効なSQLコマンドを表す文字列でない場合は、 '失敗することがあります。私はあなたの 'cmdをcmdに取り込む '行を見ていますが、それは本当に良い考えですか?その行は、実際には 'cmd'にSQLコマンドを置くか、3番目の' declare'文の* result set *を入れますか?ただ助けようとしています。アンソニー –

+0

'drop procedure if exists drop_audit_tables'を実行した後、区切り文字を変更するのを待つことがあります。 –

+0

'['+ table_name +'] 'はどこから来ますか? 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ループでこのコードを実行することです。

基本(非堅牢)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(); 

とにかくそのような何か、。 IN、OUT、またはINOUTだけPROCEDUREに対して有効であるように、パラメータを指定

MySQL: CREATE PROCEDURE

。 FUNCTIONの場合、パラメータは常にINパラメータと見なされます。

PHP Manual: PDO::prepare

はPDOStatement :: execute()メソッドによって実行されるSQL文を準備します。 SQLステートメントには、ステートメントの実行時に実際の値が代入されるゼロまたは複数の名前付き(:name)または疑問符(?)パラメーター・マーカーを含めることができます。

このような解決策は、あなたの人生を楽にします。監査テーブルを検索する基本的なクエリを定義する必要があります。少ないコード。よりシンプル。

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で注意しなければなりません。カーソルを使用し

UPDATE

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

preparable_stmt FROM stmt_nameを調製

...

preparable_stmtは、文字列リテラルまたは は、SQL文のテキストが含まれていることをユーザ変数のどちらかです。

...

+0

最初の '' TABLE_NAME''参照がどこから来ているのか聞いてみましょう。 –

+0

@AnthonyRutledge: 'TABLES'テーブルの' TABLE_NAME'列。 'SELECT \' TABLE_NAME \ 'FROM \' information_schema \ '。\' TABLES \ ';'を実行します。 – wchiquito

+0

詳細を教えてください。 –