Последовательный вызов хранимой процедуры из файла .sql - PullRequest
1 голос
/ 26 ноября 2010

Я застрял здесь.

У меня есть процедура, которую я хочу запустить X * раз подряд.(* X пару тысяч раз)
Процедура, основанная на входных данных, делает следующее:
1. Ищет action.id, если не найден LEAVE s.
2. Ищет пользователей.id, если не найден, создает его и использует LAST_INSERT_ID();
3-5.Ищет резюмируемый итог (3 типа, суммарный, дневной и ежемесячный), если он не найден, создает его и использует его идентификатор.
6. После сбора всех необходимых идентификаторов INSERT открывает новую строку в действиях и обновляетстроки суммирования в транзакции, поэтому, если какой-либо из них завершится неудачей - он ROLLBACK - не причинит вреда.
7. В зависимости от результата SELECT s сообщения.

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. Поэтому создается еще одна итоговая строка.
Я пытался отложить вызовы ...

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

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

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

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


EDIT

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


EDIT2

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


EDIT3

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

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

1 Ответ

1 голос
/ 27 ноября 2010

Это на самом деле не решение, а обходной путь.

Просто для пояснения, в сводных таблицах столбец id имеет значение PRIMARY KEY с параметром AUTO_INCREMENT и индексы для user_id и action_id column.

Мое расследование показало, что, хотя моя процедура искала запись о том, что существует с использованием WHERE user_id = u_id AND action_id = a_id в определенных ситуациях, она не нашла ее, в результате чего новая строка вставляется вместе сте же значения user_id и action_id - что-то, чего я не хотел.

Отладка процедуры показала, что искомая строка итогов, которую я искал, хотя и не была доступна с условием WHERE user_id = u_id AND action_id = a_id, была правильно возвращена при вызове ееid - PRIMARY KEY.
С этой находкой я решил изменить формат столбца id с UNASIGNED INT с AUTO_INCEREMENT на CHAR(32), который состоял из:

<user_id>|<action_id>
* 1027Это означало, что я точно знал, что такое «1028» строки, которую я хотел, еще до того, как она существовала.Это решило проблему действительно.Это также позволило мне использовать INSERT ... ON DUPLICATE KEY UPDATE ... construct.

Ниже моей обновленной процедуры:

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 есть какая-то ошибка, но я считаю, что проблема решена.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...