2016-04-14 94 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;終止準備STMT;末端循環tLoop;在13號線接近釐米」

+0

我從來沒有使用遊標,但我可以說,'PREPARE STMT FROM CMD;'可能會失敗,如果'cmd'是不是代表一個有效的SQL命令的字符串。我看到你的'cmd cmd'命令行,但是這真的是個好主意嗎?該行實際上是否將SQL命令放入'cmd'或第三個'declare'語句的*結果集*中。只是想幫助。安東尼。 –

+0

如果存在drop_audit_tables',我可能會等待運行'drop procedure'後更改分隔符。 –

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

類似的東西,反正。

MySQL: CREATE PROCEDURE

指定參數作爲IN,OUT,或INOUT只對PROCEDURE是有效的。對於功能,參數始終被視爲IN參數。

PHP Manual: PDO::prepare

準備由PDOStatement對象執行的SQL語句:: execute()方法。該SQL語句可以包含零個或多個命名(:名稱)或問號參數標記爲被執行的語句當實際值將取代(?)。

像這樣的解決方案將讓您的生活更輕鬆。您只需定義一個查找您的審計表的基本查詢。代碼較少。簡單。

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

PREPARE stmt_name FROM preparable_stmt

...

preparable_stmt是字符串字面或用戶變量 包含SQL語句的文本。

...

+0

我只會問第一個''TABLE_NAME'引用來自哪裏,允許它連接在一起。 –

+0

@AnonyRutledge:來自'TABLES'表的'TABLE_NAME'列。 'SELECT \'TABLE_NAME \'FROM \'information_schema \'。\'TABLES \';'。 – wchiquito

+0

請您詳細說明一下嗎? –