2010-11-26 77 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中的错误,但我认为问题就迎刃而解了。