我被困在这里了。
我有一个程序,我想连续运行 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没有及时更新表,所以创建的汇总行CALL
当执行第 376 行时,第 375 行中的内容尚不可见 - 因此创建另一个摘要行。
我以为我会尝试延迟通话...
B.使用mysql的SLEEP(duration)
.
这并没有改变任何事情。执行再次在同一个 CALL 处停止。
我现在没有主意了。
非常感谢建议和帮助。
NOTE:操作名称和用户名重复。
附言。请记住,这是我编写的第一个程序之一。
PS2。运行 mysql 5.1.52-community-log 64 位(Windows 7U)、PHP 5.3.2 和 Apache 2.2.17
EDIT
我已将问题中与 PHP 相关的部分删除这里有单独的问题.
EDIT2
好的,我已从 .sql 文件中删除了前 200 个调用。由于某种原因,它很好地超越了停止执行的前一行。现在它停在第 1618 行。
这意味着,在某一时刻,新的INSERTed
摘要行暂时不可见,因此当发生以下迭代之一想要SELECT
他们还无法访问它。这是 MySQL 的错误吗?
EDIT3
现在我注意到另一件有趣的事情。我调查了两个 users_summaries 的创建位置。当有两个 CALL 引用相同的内容时,就会发生这种情况(并非总是如此,但如果,那么就会发生)user
and action
非常接近。它们可能彼此相邻,也可能被 1 或 2 个不同的呼叫分开。
如果我将其中之一(在 .sql 文件内)移动 50-100 行(较早执行)就可以了。我什至设法使 .sql 文件作为一个整体工作。但这仍然没有真正解决问题。 3000 行还不错,但如果有 100000 行,我就迷路了。我不能依赖对 .sql 文件的手动调整。