2010-11-26 78 views
1

我卡在這裏。從.sql文件中順序調用存儲過程

我有一個過程,我想連續運行X *次。 (* X是幾千倍)
基於輸入數據的過程是這樣做的:
1.查找actions.id,如果找不到LEAVE s。
2.查找users.id,如果找不到,則創建一個並使用LAST_INSERT_ID(); 3-5。尋找summaries.id(3種類型,總計,每日和每月),如果沒有找到,創建一個並使用它的ID。
6.收集完所有必需的id後,將INSERT的新行記錄到操作中,並更新事務中的彙總行,如果有任何操作失敗 - 則執行ROLLBACK - 不會造成任何損害。
7.取決於結果SELECT的消息。

CREATE PROCEDURE NEW_ACTION(
    IN a_date TIMESTAMP, 
    IN u_name VARCHAR(255), 
    IN a_name VARCHAR(255), 
    IN a_chars INT, 
    IN url VARCHAR(255), 
    IN ip VARCHAR(15)) 

    lbl_proc: BEGIN 
    DECLARE a_id, u_id, us_id, usd_id, usm_id, a_day, a_month, error INT; 
    DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET error = 1; 

    SET error = 0; 
    SET a_day = DATE_FORMAT(SUBSTRING(a_date ,1,10), '%Y%m%d'); 
    SET a_month = SUBSTRING(a_day, 1, 6); 

    /* 1. RETREIVING action.id */ 
    SET a_id = (SELECT `id` FROM `actions` WHERE `name` = a_name); 
    IF a_id IS NULL THEN 
     SELECT 'error'; 
     LEAVE lbl_proc; 
    END IF; 

    /* 2. RETREIVING users.id */ 
    SET u_id = (SELECT `id` FROM `users` WHERE `name` = u_name); 
    IF u_id IS NULL THEN 
     INSERT INTO `users` (name) VALUES (u_name); 
     SET u_id = (SELECT LAST_INSERT_ID()); 
    END IF; 

    /* 3. RETREIVING user_summaries.id */ 
    SET us_id = (SELECT `id` FROM `users_summaries` WHERE `user_id` = u_id AND `action_id` = a_id); 
    IF us_id IS NULL THEN 
     INSERT INTO `users_summaries` (user_id, action_id) VALUES (u_id, a_id); 
     SET us_id = (SELECT LAST_INSERT_ID()); 
    END IF; 

    /* 4. RETREIVING user_summaries_days.id */ 
    SET usd_id = (SELECT `id` FROM `users_summaries_days` WHERE `day` = a_day AND `user_id` = u_id AND `action_id` = a_id); 
    IF usd_id IS NULL THEN 
     INSERT INTO `users_summaries_days` (day, user_id, action_id) VALUES (a_day, u_id, a_id); 
     SET usd_id = (SELECT LAST_INSERT_ID()); 
    END IF; 

    /* 5. RETREIVING user_summaries_months.id */ 
    SET usm_id = (SELECT `id` FROM `users_summaries_months` WHERE `month` = a_month AND `user_id` = u_id AND `action_id` = a_id); 
    IF usm_id IS NULL THEN 
     INSERT INTO `users_summaries_months` (month, user_id, action_id) VALUES (a_month, u_id, a_id); 
     SET usm_id = (SELECT LAST_INSERT_ID()); 
    END IF; 

    /* 6. SAVING action AND UPDATING summaries */ 
    SET autocommit = 0; 
    START TRANSACTION; 
     INSERT INTO `users_actions` (`date`, `user_id`, `action_id`, `chars`, `url`, `ip`) VALUES (a_date, u_id, a_id, a_chars, url, ip); 
     UPDATE `users_summaries` SET qty = qty + 1, chars = chars + a_chars WHERE id = us_id; 
     UPDATE `users_summaries_days` SET qty = qty + 1, chars = chars + a_chars WHERE id = usd_id; 
     UPDATE `users_summaries_months` SET qty = qty + 1, chars = chars + a_chars WHERE id = usm_id; 

     IF error = 1 THEN 
     SELECT 'error'; 
     ROLLBACK; 
     LEAVE lbl_proc; 
     ELSE 
     SELECT 'success'; 
     COMMIT; 
     END IF; 
    END; 

現在,我已經得到了原始數據,我想將此數據提供給此過程。目前大約有3000行。

我想我知道了所有的解決方案:

A. # mysql -uuser -ppass DB < calls.sql - 使用PHP我已經基本上建立這樣的呼叫列表:在

CALL NEW_ACTION('2010-11-01 13:23:00', 'username1', 'actionname1', '100', 'http://example.com/', '0.0.0.0'); 
CALL NEW_ACTION('2010-11-01 13:23:00', 'username2', 'actionname1', '100', 'http://example.com/', '0.0.0.0'); 
CALL NEW_ACTION('2010-11-01 13:23:00', 'username1', 'actionname2', '100', 'http://example.com/', '0.0.0.0'); 
... 

這總是失敗(試過幾次)第452行,它找到了兩個彙總ID(第3步)。
我認爲這可能是由於早些時候(第375-376行)存在針對同一個用戶的同一用戶的調用。
就好像mysql沒有及時更新表格一樣,因此當行376被執行時,從行375創建的CALL中的彙總行不可見 - 因此會創建另一個彙總行。
我試過延遲電話...

B.使用mysql的SLEEP(duration)
這並沒有改變任何東西。執行停止在同一個CALL處。

我現在沒有想法。
建議和幫助非常感謝。

注意:重複操作名稱和用戶名稱。

PS。請記住,這是我寫的第一個程序之一。
PS2。運行MySQL 52年5月1日 - 社區登錄64位(視窗7U),PHP 5.3.2和Apache 2.2.17


編輯

我已經刪除問題的PHP相關部分的separate question here


EDIT2

好吧,我已經刪除從.sql文件的第一個200個電話。由於某種原因,它在前一行停止執行的過程中很順利。現在它停在1618行。
這意味着,在某一時刻,一個新近的INSERTed彙總行暫時不可見,因此當它發生下列其中一個迭代想要SELECT時,它仍然無法訪問。這是一個MySQL的錯誤?


EDIT3

現在有我注意到另一個有趣的事情。我調查了兩個users_summaries創建的位置。發生這種情況(並非總是,但如果是,則是)當兩個CALL指向相近的相同useraction時。它們可以彼此相鄰,也可以由1或2個不同的呼叫分開。

如果我將其中一個(在.sql文件中)移動到低於50-100行(較早執行),那就好了。我甚至設法使.sql文件作爲一個整體工作。但是這仍然不能真正解決問題。 3000行沒有那麼糟糕,但如果我有100000,我就會迷路。我不能依靠手動調整.sql文件。

+0

@Michal M - 嘗試改變「SELECT」錯誤';「到更詳細的,如「選擇」錯誤 - RETREIVING action.id';「,這樣更容易識別哪個部分有錯誤 – ajreal 2010-11-26 10:04:19

+0

如果我們談論前兩種方法(A,B),比我知道的究竟哪一部分是。事實上,`SELECT'錯誤'`根本不會發生。在第3步我得到2行,這意味着早期的`SELECT users_summary.id`確實返回NULL而不是數字,導致第二行被插入。 – 2010-11-26 10:19:37

回答

1

這不是一個真正的解決方案,而是一種解決方法。

只是爲了澄清,彙總表有idPRIMARY KEY有兩個user_idaction_idAUTO_INCREMENT選項和索引。

我的調查顯示,雖然我的程序是尋找一個條目在某些情況下使用WHERE user_id = u_id AND action_id = a_id存在沒有發現它造成新行插入具有相同user_idaction_id值 - 這是我不想。

調試過程表明,我查找的彙總行儘管無法通過WHERE user_id = u_id AND action_id = a_id條件訪問,但在調用id - PRIMARY KEY時正確返回。
有了這個發現,我決定改變id列的格式,從UNASIGNED INTAUTO_INCEREMENTCHAR(32)其中包括了:

<user_id>|<action_id> 

這意味着我確切地知道我想要的行的id之前,甚至是存在。這真的解決了這個問題。它也使我能夠使用INSERT ... ON DUPLICATE KEY UPDATE ...結構。

下面我更新程序:

CREATE PROCEDURE `NEW_ACTION`(
    IN a_date TIMESTAMP, 
    IN u_name VARCHAR(255), 
    IN a_name VARCHAR(255), 
    IN a_chars INT, 
    IN url VARCHAR(255), 
    IN ip VARCHAR(15)) 
    SQL SECURITY INVOKER 

lbl_proc: BEGIN 
    DECLARE a_id, u_id, a_day, a_month, error INT; 
    DECLARE us_id, usd_id, usm_id CHAR(48); 
    DECLARE sep CHAR(1); 
    DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET error = 1; 

    SET sep = '|'; 
    SET error = 0; 
    SET a_day = DATE_FORMAT(SUBSTRING(a_date ,1,10), '%Y%m%d'); 
    SET a_month = SUBSTRING(a_day, 1, 6); 

    /* RETREIVING action.id */ 
    SET a_id = (SELECT `id` FROM `game_actions` WHERE `name` = a_name); 
    IF a_id IS NULL THEN 
     SELECT 'error'; 
     LEAVE lbl_proc; 
    END IF; 

    /* RETREIVING users.id */ 
    SET u_id = (SELECT `id` FROM `game_users` WHERE `name` = u_name); 
    IF u_id IS NULL THEN 
     INSERT INTO `game_users` (name) VALUES (u_name); 
     SET u_id = LAST_INSERT_ID(); 
    END IF; 

    /* SETTING summaries ids */ 
    SET us_id = CONCAT(u_id, sep, a_id); 
    SET usd_id = CONCAT(a_day, sep, u_id, sep, a_id); 
    SET usm_id = CONCAT(a_month, sep, u_id, sep, a_id); 

    /* SAVING action AND UPDATING summaries */ 
    SET autocommit = 0; 
    START TRANSACTION; 
     INSERT INTO `game_users_actions` (`date`, `user_id`, `action_id`, `chars`, `url`, `ip`) 
     VALUES (a_date, u_id, a_id, a_chars, url, ip); 
     INSERT INTO `game_users_summaries` (`id`, `user_id`, `action_id`, `qty`, `chars`) 
     VALUES (us_id, u_id, a_id, 1, a_chars) 
     ON DUPLICATE KEY UPDATE qty = qty + 1, chars = chars + a_chars; 
     INSERT INTO `game_users_summaries_days` (`id`, `day`, `user_id`, `action_id`, `qty`, `chars`) 
     VALUES (usd_id, a_day, u_id, a_id, 1, a_chars) 
     ON DUPLICATE KEY UPDATE qty = qty + 1, chars = chars + a_chars; 
     INSERT INTO `game_users_summaries_months` (`id`, `month`, `user_id`, `action_id`, `qty`, `chars`) 
     VALUES (usm_id, a_month, u_id, a_id, 1, a_chars) 
     ON DUPLICATE KEY UPDATE qty = qty + 1, chars = chars + a_chars; 

     IF error = 1 THEN 
     SELECT 'error'; 
     ROLLBACK; 
     LEAVE lbl_proc; 
     ELSE 
     SELECT 'success'; 
     COMMIT; 
     END IF; 
    END 

不管怎樣,我還是覺得有某種在MySQL中的錯誤,但我認爲問題就迎刃而解了。