2009-11-05 74 views
2

我需要使用一個變量來指示在遊標聲明中要查詢的數據庫。下面是代碼的一小段:MySQL存儲過程:在遊標聲明中使用變量作爲數據庫名稱

CREATE PROCEDURE `update_cdrs_lnp_data`(IN dbName VARCHAR(25), OUT returnCode SMALLINT) 

cdr_records:BEGIN 

DECLARE cdr_record_cursor CURSOR FOR 

SELECT cdrs_id, called, calling FROM dbName.cdrs WHERE lrn_checked = 'N'; 

# Setup logging 
DECLARE EXIT HANDLER FOR SQLEXCEPTION 
BEGIN 
     #call log_debug('Got exception in update_cdrs_lnp_data'); 
     SET returnCode = -1; 
END; 

正如你所看到的,我試圖使用變量DBNAME以指示數據庫查詢應內發生。但是,MySQL不會允許這樣做。我也試過這樣的東西,如:

CREATE PROCEDURE `update_cdrs_lnp_data`(IN dbName VARCHAR(25), OUT returnCode SMALLINT) 

cdr_records:BEGIN 

DECLARE cdr_record_cursor CURSOR FOR 

     SET @query = CONCAT("SELECT cdrs_id, called, calling FROM " ,dbName, ".cdrs WHERE lrn_checked = 'N' "); 
     PREPARE STMT FROM @query; 
     EXECUTE STMT; 

# Setup logging 
DECLARE EXIT HANDLER FOR SQLEXCEPTION 
BEGIN 
     #call log_debug('Got exception in update_cdrs_lnp_data'); 
     SET returnCode = -1; 
END; 

當然,這不起作用,因爲MySQL只允許在遊標聲明中的標準SQL語句。

任何人都可以通過傳入應該受到影響的數據庫的名稱來思考一種方法來在多個數據庫中使用相同的存儲過程嗎?

回答

7

Vijay Jadhav的答案是解決MySQL限制的正確方法。實際上,你需要3個proc來完成它:

proc1使用Vijay Jadhav的方式,像數據收集器一樣工作。您需要將變量傳遞給proc1,並讓它爲proc2創建tmp表。 Vijay有一個限制,他應該使用「CREATE TEMPORARY TABLE tmp_table_name SELECT ...」創建一個TEMPORARY表。因爲臨時表是線程安全的。

proc2在由proc1創建的tmp表中聲明遊標。由於tmp表已經是已知的並且硬編碼到聲明中,所以沒有更多的「找不到表」錯誤。

proc3像一個「主」功能一樣工作,所有參數需要發送到proc1和proc2。 proc3只是簡單地調用proc1,然後proc2使用每個proc所需的參數。

p.s需要將系統變量「sql_notes」設置爲0,否則proc1將停止在DROP TABLE命令上。

這是我的例子:

CREATE PROCEDURE `proc1`(SourceDBName CHAR(50), SourceTableName CHAR(50)) 
BEGIN 
    DECLARE SQLStmt TEXT; 

    SET @SQLStmt = CONCAT('DROP TEMPORARY TABLE IF EXISTS tmp_table_name'); 
    PREPARE Stmt FROM @SQLStmt; 
    EXECUTE Stmt; 
    DEALLOCATE PREPARE Stmt; 

    SET @SQLStmt = CONCAT('CREATE TEMPORARY TABLE tmp_table_name SELECT ... FROM ',SourceDBName,'.',SourceTableName,' WHERE ... '); 
    PREPARE Stmt FROM @SQLStmt; 
    EXECUTE Stmt; 
    DEALLOCATE PREPARE Stmt; 
END$$ 

CREATE PROCEDURE `proc2`(TargetDBName CHAR(50), TargetTemplateTableName CHAR(50)) 
BEGIN 
    DECLARE done INT DEFAULT 0; 
    DECLARE FieldValue CHAR(50); 
    DECLARE CursorSegment CURSOR FOR SELECT ... FROM tmp_table_name; 
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; 

    OPEN CursorSegment; 
    REPEAT 
    FETCH CursorSegment INTO FieldValue; 
    IF NOT done THEN 
     ... 
    END IF; 
    UNTIL done END REPEAT; 
    CLOSE CursorSegment; 
END$$ 

CREATE PROCEDURE `proc3`(SourceDBName CHAR(50), SourceTableName CHAR(50), TargetDBName CHAR(50), TargetTemplateTableName CHAR(50)) 
BEGIN 
    CALL proc1(SourceDBName, SourceTableName); 
    CALL proc2(TargetDBName, TargetTemplateTableName); 
END$$ 
1

不,你不能在遊標中做到這一點。 也許只是準備好的陳述可能會做這份工作? :

delimiter ;; 

create procedure test(in dbName varchar(40)) 
begin 

set @query := CONCAT("SELECT * FROM " , dbName, ".db;"); 

PREPARE s from @query; 

EXECUTE s; 
DEALLOCATE PREPARE s; 

end;; 

delimiter ; 

call test("mysql"); 
+0

noonex - 這似乎是一個好主意。但是,我如何迭代結果呢? – Justin 2009-11-05 15:55:14

0

對此的答案是它不能完成。你不能在遊標聲明中使用變量。我很欣賞noonex的迴應。但是,他的解決方案不允許我瀏覽結果。它只是執行查詢。

2

嘗試在不同的過程中使用準備語句創建(臨時)表。

SET @query = CONCAT("CREATE TABLE temp_table AS SELECT cdrs_id, called, calling FROM "  ,dbName, ".cdrs WHERE lrn_checked = 'N' "); 

...

,然後從該表中的 '測試' 程序選擇數據。

0

創建程序測試(在數據庫名VARCHAR(40)) READS SQL DATA < - 這條線的回報將允許您通過結果走 開始 。 .. $ result = call test(「mysql」);