И нет, мне не нравится это решение, но это был единственный способ заставить его работать.

трял здесь.

У меня есть процедура, которую я хочу запустить X * раз подряд. (* X пару тысяч раз)
Процедура на основе входных данных делает это:
1. Ищет action.id, если не найденLEAVEs.
2. Ищет users.id, если не найден, создает его и используетLAST_INSERT_ID();
3-5. Ищет файл summary.id (3 типа, общий, ежедневный и ежемесячный), если он не найден, создает его и использует его идентификатор.
6. Как только все необходимые идентификаторы собраны,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, где были найдены два итоговых идентификатора (шаг 3).
Я думал, что это может быть связано с тем, что ранее (строки 375-376) есть вызовы для одного и того же пользователя для одного и того же действия.
Как будто mysql не обновлял таблицы вовремя, поэтому итоговая строка, созданная вCALL из строки 375 еще не видно, когда строка 376 выполняется, поэтому создается еще одна итоговая строка.
Пытался задержать звонки ...

Б. Использование MySQLSLEEP(duration).
Это ничего не изменило. Исполнение снова останавливается на том же CALL.

У меня сейчас нет идей.
Предложения и помощь очень ценятся.

НОТА: имена действий и имена пользователей повторяются.

PS. Имейте в виду, что это одна из моих первых процедур, когда-либо написанных.
PS2. Запуск mysql 5.1.52-community-log 64bit (Windows 7U), PHP 5.3.2 и Apache 2.2.17

РЕДАКТИРОВАТЬ

Я удалил связанную с PHP часть вопросаотдельный вопрос здесь.

EDIT2

Хорошо, я удалил первые 200 звонков из файла .sql. По какой-то причине все прошло хорошо после предыдущей строки, которая прекращала выполнение. Теперь он остановился на ряду 1618.
Это будет означать, что в какой-то момент недавноINSERTed итоговая строка на мгновение не видна, поэтому, когда случается, что одна из следующих итерацийSELECT это, это еще не доступно для них. Это ошибка MySQL?

EDIT3

Теперь есть еще одна интересная вещь, которую я заметил. Я исследовал, где создаются два users_summaries. Это происходит (не всегда, но если, тогда это так), когда есть два ВЫЗОВА, ссылающиеся на один и тот жеuser а такжеaction в непосредственной близости. Они могут быть рядом друг с другом или разделены 1 или 2 разными вызовами.

Если я переместу один из них (в пределах файла .sql) примерно на 50-100 строк ниже (выполнено раньше), то это нормально. Мне даже удалось заставить файл .sql работать в целом. Но это все еще не решает проблему. С 3000 рядами это не так уж плохо, но если бы у меня было 100000, я потерялся. Я не могу полагаться на ручные настройки в .sql файле.

Ответы на вопрос(1)

Ваш ответ на вопрос