SQLinfo.ru - Все о MySQL Webew.ru: теория и практика веб-технологий

Форум пользователей MySQL

Задавайте вопросы, мы ответим

Вы не зашли.

#51 24.04.2010 22:20:11

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Разные вопросы, в основном по процедурам

А, насчёт BEGIN, кажется понял:

A new savepoint level is created when a stored function is invoked or a trigger is activated. The savepoints on previous levels become unavailable and thus do not conflict with savepoints on the new level.

При входе в функцию, тоже создаётся что-то вроде "внутренней" точки сохранения или нет?

Отредактированно Артём Н. (24.04.2010 22:20:59)


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#52 24.04.2010 22:28:03

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Разные вопросы, в основном по процедурам

Нет, на диске во временном каталоге.

Этот запрос выводит вообще произвольную строку, т.к. у Вас нет ORDER BY.
В случае с сортировкой и наличием индекса — оно выведет нужную строку.

Нет, такой переменной нет. В любом случае, это плохое решение, если Вам
нужно это определять где-то внутри.

Вы не сможете обойти ограничения хранимых функций — такую Вы просто
не создадите.

Неактивен

 

#53 24.04.2010 22:40:04

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Разные вопросы, в основном по процедурам

Этот запрос выводит вообще произвольную строку, т.к. у Вас нет ORDER BY.
В случае с сортировкой и наличием индекса — оно выведет нужную строку.

Но запрос я взял с хабра... Разьве LIMIT a,b Не должен выводить строки, начиная с номера a, в количестве b, независимо от индексов?

Нет, такой переменной нет. В любом случае, это плохое решение, если Вам
нужно это определять где-то внутри.

Угу. sad А что же делать? Ведь, если begin влияет на транзакцию, то либо мне придётся явно её начинать (что также плохо, поскольку, транзакция должна начинаться процедурой автоматически), либо как-то определять начата ли она. Разьве есть варианты?

Вы не сможете обойти ограничения хранимых функций — такую Вы просто
не создадите.

В смысле? Функция, которую я вам привёл, у меня уже создана и сохранена в БД.
Ведь вызов пользовательских функций, не влияет на транзакции? Меня просто BEGIN смутил...

Отредактированно Артём Н. (24.04.2010 22:42:48)


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#54 24.04.2010 23:13:28

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Разные вопросы, в основном по процедурам

Запрос вида «SELECT … FROM tablename» по стандарту выводит строки в произвольном
порядке. Более того, не гарантируется, что повторный тот же самый запрос выведет
их в том же порядке (и это реально так — например, в NDB строки реально всегда
выводятся в разном порядке). Для того, чтобы строки выводились в одном порядке,
всегда нужно применять ORDER BY. А раз есть сортировка — можно использовать индекс.

Запишите на входе в процедуру точку сохранения?

Эээ... как Вам удалось сделать функцию, которая вызывает процедуру?

Неактивен

 

#55 25.04.2010 00:15:22

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Разные вопросы, в основном по процедурам

Запрос вида «SELECT … FROM tablename» по стандарту выводит строки в произвольном
порядке. Более того, не гарантируется, что повторный тот же самый запрос выведет
их в том же порядке (и это реально так — например, в NDB строки реально всегда
выводятся в разном порядке). Для того, чтобы строки выводились в одном порядке,
всегда нужно применять ORDER BY. А раз есть сортировка — можно использовать индекс.

Хм... Не знал. Спасибо. Но там индекс-то использовать - дольше создавать.
Пять записей - максимум в таблице. Реально, конечно, возможно добавить сколько угодно, но форма бланка ограничивает.

Запишите на входе в процедуру точку сохранения?

Так мне не нужно ворачивать к точке сохранения.
Мне нужно, чтобы не было повторного вызова start transaction,
при незавершённой текущей транзакции.

Эээ... как Вам удалось сделать функцию, которая вызывает процедуру?

Ну да... Неужели, я не такой как все..?! big_smile Разьве есть какие-то ограничения на вызов процедур из функций?

Вот, вызываемая процедура:

CREATE
DEFINER = 'root'@'%'
PROCEDURE GetCurUser(OUT user_name CHAR(16), OUT host_name CHAR(60))
READS SQL DATA
SQL SECURITY INVOKER
COMMENT 'Получение текущего пользователя'
BEGIN
  select substring_index(current_user(),"@", 1),
    substring_index(current_user(),"@", -1) into user_name, host_name;
END;

И, вроде бы, работает:

Код:

mysql> select GetCurUserFullname();
+----------------------+
| GetCurUserFullname() |
+----------------------+
| main root            |
+----------------------+
1 row in set (0.00 sec)

Отредактированно Артём Н. (25.04.2010 00:16:32)


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#56 25.04.2010 00:31:22

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Разные вопросы, в основном по процедурам

А, вообще, не гарантируется, что записи таблицы будут возвращены именно в том порядке, в котором были вставлены?


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#57 25.04.2010 16:50:37

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Разные вопросы, в основном по процедурам

Запишите на входе в процедуру точку сохранения?

В том-то всё и дело, что мне не нужна точка сохранения, поскольку внутри транзакции нет откатов.
Вся моя "система" сводится к одной функции - добавлению договоров в БД.
А уже вокруг "навешано" всё остальное.
Даже вся остальная работа с договорами. Изменение/пролонгация/замена - это тоже самое добавление, только вместо insert, там update.

В принципе, ничего сложного в этой процедуре нет, за исключением того,
что с клиентом могут работать несколько пользователей. А я запутался с транзакциями.

Конкретно, я запутался с уровнями изоляции. Поскольку, видимо, здесь внутри транзакции надо менять уровень изоляции.

Я не уверен в том, что:
1.) Внутри транзакции, мне будет дан правильный ID водителя, при переносе из временной таблицы в БД. ID вычисляется, как следующий за максимальным ID в БД.
Что будет, если после расчёта ID текущим пользователем, другой пользователь добавит водителя в таблицу, а, затем, водителя добавит текущий пользователь.
2.) Правильный расчёт ID. Увидит ли другой пользователь, что водитель был добавлен текущим, если транзакция не была завершена (опять же, не знаю какой уровень изоляции и когда требуется)?
3.) Резервирование бланка. Какой нужно установить уровень изоляции, чтобы другой пользователь увидел, что бланк уже был зарезервирован текущим?

Может, вы сможете мне помочь определить какие нужны здесь уровни изоляции?

Вот общий алгоритм:

Код:

Добавление договора:
  1.) Открываю транзакцию.
  2.) Заношу сопутствующие данные:
    а.) Список водителей в БД (открывает транзакцию). Через процедуру DriverAdd.
    б.) Резервирую бланк (открывает транзакцию). BlankReserve.
      Всем соединениям должно быть известно, что данный бланк зарезервирован.
      Если транзакция не завершается, бланк нужно освободить.
  3.) Заношу договор в БД.
    1. Закрепляю бланк за договором.
    2. Вставляю договор.
    3. Переношу список водителей в БД.
  4.) Завершаю транзакцию.

Код процедур:

Код:

/*==============================================================*/
/* Procedure : BlankReserve                                     */
/*==============================================================*/

DROP PROCEDURE IF EXISTS BlankReserve;

CREATE
DEFINER = 'root'@'%'
PROCEDURE BlankReserve(
  IN v_id_ins_company INT,
  IN v_dog_ser VARCHAR(10), IN v_dog_num VARCHAR(25),
  OUT err_code TINYINT)
SQL SECURITY DEFINER
COMMENT 'Предположительные привилегии: пользователь.'
sproc: BEGIN
  declare granted BOOL;
  declare user_name CHAR(16);
  declare user_host CHAR(60);
  set err_code := 1;

  # Проверяю полномочия
  select ifnull(PRIV_CONT_ADD = true, false) from user_groups
    where ID_GROUP = GetGroupID() into granted;
  if (not granted) then
    leave sproc;
  end if;

  # Проверяю наличие пустого незарезервированного бланка
  select count(*) != 0 into granted from blanks_journal where
    DOG_SER = v_dog_ser and DOGNUMB = v_dog_num
    and ID_INSURANCE_COMPANY = v_id_ins_company
    and USER_RESERVER is NULL and HOST_RESERVER is NULL
    and ID_BSO_STATUS = 1;

  if (not granted) then
    leave sproc;
  end if;

  call GetCurUser(user_name, user_host);

  update blanks_journal set
    USER_RESERVER       = user_name,
    HOST_RESERVER       = user_host,
    DATE_UPDATE         = CURDATE()
  where
    DOG_SER = v_dog_ser and DOGNUMB = v_dog_num
    and ID_INSURANCE_COMPANY = v_id_ins_company;

  set err_code := 0;
END;

/*==============================================================*/
/* Procedure : BlankUnreserve                                   */
/*==============================================================*/

DROP PROCEDURE IF EXISTS BlankReserve;

CREATE
DEFINER = 'root'@'%'
PROCEDURE BlankUnreserve(
  IN v_id_ins_company INT,
  IN v_dog_ser VARCHAR(10), IN v_dog_num VARCHAR(25),
  OUT err_code TINYINT)
SQL SECURITY DEFINER
COMMENT 'Предположительные привилегии: пользователь.'
sproc: BEGIN
  declare granted BOOL;
  declare user_name CHAR(16);
  declare user_host CHAR(60);
  # Данные для udpate ... where
  declare real_id_ins_company INT default v_id_ins_company;
  declare real_dog_ser VARCHAR(10) default v_dog_ser;
  declare real_dog_num VARCHAR(25) default v_dog_num;

  set err_code := 1;

  # Проверяю полномочия
  select ifnull(PRIV_CONT_ADD = true, false) from user_groups
    where ID_GROUP = GetGroupID() into granted;
  if (not granted) then
    leave sproc;
  end if;

  call GetCurUser(user_name, user_host);

  # Проверяю наличие данного зарезервированного бланка.

  if (v_dog_ser is NULL or v_dog_num is NULL or v_id_ins_company is NULL) then
  # Если заданы NULL, то проверяю резервировал ли этот пользователь какие-либо
  # бланки, вообще. Выбираю самый новый.
    select count(*) != 0, ID_INSURANCE_COMPANY, DOG_SER, DOGNUMB
      into granted, real_id_ins_company, real_dog_ser, real_dog_num
      from blanks_journal where 
      USER_RESERVER = user_name and HOST_RESERVER = user_host
      and ID_BSO_STATUS = 1
      order by DATE_UPDATE desc limit 1;
  else
    select BlankIsReserved(v_dog_ser, v_dog_num, v_id_ins_company) into granted;
  end if;

  if (not granted) then
    leave sproc;
  end if;

  update blanks_journal set
    USER_RESERVER       = NULL,
    HOST_RESERVER       = NULL,
    DATE_UPDATE         = CURDATE()
  where
    DOG_SER = real_dog_ser and DOGNUMB = real_dog_num
    and ID_INSURANCE_COMPANY = real_id_ins_company;

  set err_code := 0;
END;

/*==============================================================*/
/* Procedure : DriverAdd()                                      */
/*==============================================================*/

DROP PROCEDURE IF EXISTS DriverAdd;

CREATE
DEFINER = 'root'@'%'
PROCEDURE DriverAdd(
  IN v_id_client DECIMAL(23, 0),
  OUT err_code TINYINT)
SQL SECURITY DEFINER
COMMENT 'Предположительные привилегии: пользователь.'
sproc: BEGIN
  declare granted BOOL;

  set err_code := 1;

  # Проверяю полномочия
  select ifnull(PRIV_CONT_ADD = true, false) or
    ifnull(PRIV_CONT_CHG = true, false) from user_groups
    where ID_GROUP = GetGroupID() into granted;
  if (not granted) then
    leave sproc;
  end if;

  create temporary table if not exists drivers_in_contract
  (
    ID_CLIENT DECIMAL(23, 0),
    primary key(ID_CLIENT)
  ) ENGINE=InnoDB;
  # Не MEMORY. Транзакции.
  # replace select * from drivers limit 0;

  insert into drivers_in_contract values(v_id_client);

  set err_code := 0;
END;

/*==============================================================*/
/* Function : GetNextIDDriver()                                 */
/*==============================================================*/

DROP FUNCTION IF EXISTS GetNextIDDriver;

CREATE
DEFINER = 'root'@'%'
FUNCTION GetNextIDDriver()
RETURNS DECIMAL(23, 0)
READS SQL DATA
SQL SECURITY INVOKER
COMMENT 'Возвращает свободный ID водителя. Привилегии вызывающего.'
BEGIN
  declare result DECIMAL(23, 0);
  select ifnull(max(ID_DRIVER) + 1, 0) into result from drivers;
  return result;
END;

/*==============================================================*/
/* Procedure : ContAdd()                                        */
/*==============================================================*/

DROP PROCEDURE IF EXISTS ContAdd;

CREATE
DEFINER = 'root'@'%'
PROCEDURE ContAdd(
  IN v_dog_ser VARCHAR(10),
  IN v_dog_numb VARCHAR(25),
  IN v_date_start DATE,
  IN v_date_end DATE,
  IN v_start_use DATE,
  IN v_end_use DATE,
  IN v_start_use1 DATE,
  IN v_end_use1 DATE,
  IN v_start_use2 DATE,
  IN v_end_use2 DATE,
  IN v_comment VARCHAR(255),
  IN v_id_client VARCHAR(22),
  IN v_id_insurance_company INT,
  IN v_transit BOOL,
  IN v_id_car VARCHAR(22),
  IN v_id_territory_use INT,
  IN v_ins_sum FLOAT,
  IN v_ins_prem FLOAT,
  IN v_koef_ter FLOAT,
  IN v_koef_bonus_malus FLOAT,
  IN v_koef_stag FLOAT,
  IN v_koef_unlimited FLOAT,
  IN v_koef_power FLOAT,
  IN v_koef_period_use FLOAT,
  IN v_koef_srok_ins FLOAT,
  IN v_koef_kn FLOAT,
  IN v_base_sum FLOAT,
  IN v_unlimited_drivers BOOL,
  IN v_date_write DATE,
  IN v_date_begin DATE,
  IN v_id_insurance_class INT,
  OUT v_id_contract DECIMAL(23, 0),
  OUT err_code TINYINT
)
SQL SECURITY DEFINER
COMMENT 'Предположительные привилегии: пользователь.'
sproc: BEGIN
  declare granted BOOL;
  declare user_name CHAR(16);
  declare user_host CHAR(60);
  declare creating_date DATE default CURDATE();
  declare i INT;
  declare ccln_id DECIMAL(23, 0);
  declare ccont_id DECIMAL(23, 0);
  # Обработка отсутствия временной таблицы с водителями.
  DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02' SET i := 0;

  set err_code := 1;

  # Проверяю полномочия.
  select ifnull(PRIV_CONT_ADD = true, false) from user_groups
    where ID_GROUP = GetGroupID() into granted;
  if (not granted) then
    leave sproc;
  end if;

  call GetCurUser(user_name, user_host);

  # Проверяю серию/номер на соответствие зарезервированным.
  # Выбираю неиспользованный бланк, с заданными номерами, зарезервированный
  # текущим пользователем.
  select BlankIsReserved(v_id_insurance_company, v_dog_ser, v_dog_numb)
    into granted;

  if (not granted) then
    leave sproc;
  end if;

  # Если имеются права на изменение договора, проверка дат пропускается.
  select ifnull(PRIV_CONT_CHG = true, false) from user_groups
    where ID_GROUP = GetGroupID() into granted;
  if (not granted) then
    # Даты начала нельзя вводить задним числом.
    set granted := (v_date_begin >= creating_date)
      and (v_date_start >= creating_date);
    # Период не может быть больше срока действия.
    set granted :=
      granted and (v_date_start < v_date_end)
      and (v_start_use >= v_date_start)
      and (v_start_use < v_end_use)
      and (v_end_use <= v_date_end);
    # Прочие проверки.
    set granted :=
      granted
      and (((v_start_use1 < v_end_use1) and (v_end_use1 <= v_start_use2)
        and (v_end_use1 <= v_date_end))
        or ((v_start_use1 is NULL) and (v_end_use1 is NULL)))
      and (((v_start_use2 < v_end_use2) and (v_end_use <= v_start_use1)
        and (v_end_use2 <= v_date_end))
        or ((v_start_use2 is NULL) and (v_end_use2 is NULL)))
      and (v_date_write <= v_date_begin);

    if (not granted) then
      leave sproc;
    end if;
  end if;


  set autocommit = false;
  start transaction;

  select ifnull(max(ID_DOGOVOR) + 1, 0) into ccont_id from dogovor;

  insert into dogovor(ID_DOGOVOR, DOG_SER, DOGNUMB, DATE_DOG_CREATE,
    DATE_START, DATE_END,
    START_USE, END_USE, START_USE1, END_USE1, START_USE2, END_USE2,
    `COMMENT`, ID_CLIENT, ID_DOGOVOR_TYPE, ID_INSURANCE_COMPANY, TRANSIT,
    ID_CAR, ID_TERRITORY_USE, INS_SUM, INS_PREM, KOEF_TER, KOEF_BONUSMALUS,
    KOEF_STAG, KOEF_UNLIMITED, KOEF_POWER, KOEF_PERIOD_USE, KOEF_SROK_INS,
    KOEF_KN, BASE_SUM, UNLIMITED_DRIVERS, DATE_WRITE, DATE_BEGIN,
    ID_INSURANCE_CLASS, DATE_INSERT, USER_INSERT_NAME)
  values(ccont_id, v_dog_ser, v_dog_numb,
    creating_date, v_date_start, v_date_end,
    v_start_use, v_end_use, v_start_use1, v_end_use1, v_start_use2, v_end_use2,
    v_comment, v_id_client, 1, v_id_insurance_company, v_transit, v_id_car,
    v_id_territory_use, v_ins_sum, v_ins_prem, v_koef_ter, v_koef_bonus_malus,
    v_koef_stag, v_koef_unlimited, v_koef_power, v_koef_period_use,
    v_koef_srok_ins, v_koef_kn, v_base_sum, v_unlimited_drivers,
    v_date_write, v_date_begin,
    v_id_insurance_class, creating_date, GetCurUserFullname());

  # Перенос временного списка клиентов в список водителей по договору.
  # Если таблица отсутствует, HANDLER устанавливает i = 0.
  select COUNT(*) into i from drivers_in_contract;
  while (i > 0) do
    select ID_CLIENT into ccln_id from drivers_in_contract
      order by ID_CLIENT asc limit 0, 1;
    insert into drivers(ID_DRIVER, ID_CAR, ID_CLIENT, ID_DOGOVOR, DATE_INSERT)
      values(GetNextIDDriver(), v_id_car, ccln_id, ccont_id, CURDATE());
    delete from drivers_in_contract where ID_CLIENT = ccln_id;
    set i := i - 1;
  end while;

  update blanks_journal set
    ID_BSO_STATUS = 2
  where
    DOG_SER = v_dog_ser and DOGNUMB = v_dog_numb
    and ID_INSURANCE_COMPANY = v_id_insurance_company;

  commit;
  set v_id_contract := ccont_id;
  set err_code      := 0;
END;

Отредактированно Артём Н. (25.04.2010 16:51:15)


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#58 25.04.2010 17:04:58

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Разные вопросы, в основном по процедурам

Хм, действительно, раньше вообще нельзя было вызывать процедуры из функций,
теперь она просто возвращает ошибку при попытке вызвать запрещенные действия.
В любом случае, начать транзакцию не получится smile

Порядок не гарантируется. Т.е., конечно, никто специально порядок менять не будет,
но полагаться на это все равно не следует.

Неактивен

 

#59 25.04.2010 17:24:18

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Разные вопросы, в основном по процедурам

Уровень изоляции внутри транзакции менять нельзя, это нужно делать независимыми
транзакциями.

Добавление водителя никак не привязано к бланкам, поэтому водители должны доба-
вляться независимыми транзакциями (например, в отдельных соединениях). Уровень
изоляции не важен, т.к. это атомарные операции добавления.

Удаление бланка из списка возможных бланков — READ UNCOMMITTED (тогда Вы бу-
дете видеть, что кто-то удалил в других транзакциях этот бланк) и прямой DELETE в
базу с проверкой количества удаленных строк (если 0 — значит, кто-то бланк уже
занял и нужно занять другой).

Заведение договора — занесение новой строки в договоры с соответствующими ID
водителей и договоров в транзакции, которая удалила бланк.

Неактивен

 

#60 25.04.2010 22:44:14

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Разные вопросы, в основном по процедурам

В любом случае, начать транзакцию не получится

Да я спрашивал про то, не подтверждает ли BEGIN в начале процедуры, текущую транзакцию. smile

Уровень изоляции внутри транзакции менять нельзя, это нужно делать независимыми транзакциями.

Хм, вроде, я где-то читал, что возможно. Хотя, наверное, это не про MySQL там было.

Добавление водителя никак не привязано к бланкам, поэтому водители должны добавляться независимыми транзакциями (например, в отдельных соединениях). Уровень изоляции не важен, т.к. это атомарные операции добавления.

Проблема-то в том, что водители должны существовать только в рамках добавления договора.
Т.е., пользователь на клиенте как-угодно меняет список водителей.
Затем, нажимает Ok. После этого инициируется добавление договора.
Вначале идентификаторы клиентов, которые содержат водители, в цикле заносятся во временную таблицу.
После этого, вызывается процедура добавления договора, которая, в цикле добавляет водителей в реальную таблицу и очищает временную.

Проблема в том, что данная процедура формирует ID для каждого водителя, перед тем, как занести его в таблицу.

Вопрос заключается в том может ли другой пользователь "вклиниться" между получением ID и записью в таблицу БД?
И, если может, то как с этим бороться?

Удаление бланка из списка возможных бланков

Он не удаляет, а только флаг ставит.

READ UNCOMMITTED (тогда Вы бу-
дете видеть, что кто-то удалил в других транзакциях этот бланк) и прямой DELETE в
базу с проверкой количества удаленных строк (если 0 — значит, кто-то бланк уже
занял и нужно занять другой).

Delete не подходит... Что делать в таком случае?
Т.е., я например, проверяю: "select - да, бланк свободен." Делаю Update set status = 2.
Но в это время (между select и update), могло тоже произойти в параллельной транзакции другого пользователя?
Т.е., два пользователя будут считать, что один и тот же бланк свободен?
Нужно тогда блокировку использовать? sad

Реально, конечно, это почти невероятно. Но для ID водителей тот же самый вопрос.
А с ID очень даже вероятно, что такая ситуация может получиться.

Заведение договора — занесение новой строки в договоры с соответствующими ID
водителей и договоров в транзакции, которая удалила бланк.

А где открывать транзакцию? Ведь пользователь может забыть выбрать договор, а, затем нажать Ok. Конечно, возможно на клиенте проверять, но...
Лучше открывать транзакцию и в AddDrivers и в BlankReserve. Только как?

Спасибо. smile Постепенно проясняется что к чему.

Отредактированно Артём Н. (25.04.2010 22:47:36)


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#61 26.04.2010 21:37:20

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Разные вопросы, в основном по процедурам

Получить повторный ID не получится никак — за этим следят wink

Ну, сделайте UPDATE и посмотрите количество обновленных строк.

Написать за Вас программу я не хочу smile

Неактивен

 

#62 27.04.2010 00:56:23

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Разные вопросы, в основном по процедурам

Получить повторный ID не получится никак — за этим следят

Хм... Мда, понятно. Хотите сказать, что нужно обрабатывать ошибку, которая возникнет, если такой ID уже существует? :-\ А проще нет возможности это сделать?

Ну, сделайте UPDATE и посмотрите количество обновленных строк.

А как узнать количество обновлённых строк в хранимой процедуре, например?

Написать за Вас программу я не хочу

А я так надеялся, так надеялся... big_smile
Я и не прошу. Программа уже почти написана. smile
У меня остались именно вопросы по транзакциям и взаимодействию пользователей в MySQL.
Тем не менее, вы мне уже и так очень сильно помогли. smile


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#63 27.04.2010 10:46:54

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Разные вопросы, в основном по процедурам

Если у Вас используются автоинкрементные ID, то за этим следят. А иначе, конечно,
надо сделать уникальный индекс (и тогда за ним будут тоже следить, и вставить стро-
ку с одинаковым ID не дадут).

В хранимой процедуре — ROW_COUNT().

Неактивен

 

#64 27.04.2010 14:24:17

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Разные вопросы, в основном по процедурам

Если у Вас используются автоинкрементные ID, то за этим следят.

Как ни печально, last_id имеет тип BIGINT. sad С DECIMAL не работает.

Поэтому, вычисляю я ID таким вот макаром:
select ifnull(max(ID_DOGOVOR) + 1, 0) into ccont_id from dogovor;

С бланками - понятно.

А вот с водителями и договорами, меня волнует может ли наступить ситуация, когда два пользователя, работая параллельно попытаются вставить договора с одинаковым ID.

А иначе, конечно,
надо сделать уникальный индекс (и тогда за ним будут тоже следить, и вставить строку с одинаковым ID не дадут).

ID_DOGOVOR объявлен, как primary key. Вставить-то не дадут...
Только, мне, в любом случае, надо занести договор в БД.
Думаю, что с HANDLER это возможно сделать. Но, опять же, во-первых, - это сложно.
Во-вторых, а при использовании HANDLER, "внутри" его может возникнуть что-то вроде "гонки", когда ID начнут бесконечно увеличиваться? Или нет?

Отредактированно Артём Н. (27.04.2010 14:24:33)


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#65 27.04.2010 14:34:30

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Разные вопросы, в основном по процедурам

Ох, с "гонкой" чего-то меня подглючило. Не может.
Просто один вставит в таблицу запись с данным ID и продолжит выполнение.
А второй не сможет. Увеличит ID и вставит запись.


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#66 27.04.2010 15:23:45

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Разные вопросы, в основном по процедурам

Именно так smile

Неактивен

 

#67 28.04.2010 21:17:07

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Разные вопросы, в основном по процедурам

А LOCK TABLES - плохой вариант?


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#68 28.04.2010 21:30:29

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Разные вопросы, в основном по процедурам

Хороший, если не забудете UNLOCK сделать. Ну и надо понимать, что блокируете
всю таблицу целиком, т.е. Вы эффективно сведете работу к однопользовательской.

Неактивен

 

#69 28.04.2010 21:37:11

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Разные вопросы, в основном по процедурам

Хм... Мда... sad Не вариант. Но, ради интереса, при окончании сессии, UNLOCK делается автоматически?


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#70 28.04.2010 21:41:27

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Разные вопросы, в основном по процедурам

Да, если отсоединитесь — то да.

Неактивен

 

#71 28.04.2010 21:44:31

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Разные вопросы, в основном по процедурам

И ещё вопросы. С уровнями изоляции, вроде бы, разобрался.
Но, почему-то у меня, после установки уровня изоляции (set transaction isolation level ...), он всегда сбрасывается в repeatable-read.
Т.е., не распространяется на следующую транзакцию.
А на сессию возможно его установить (не глобально)?

И когда начинать транзакцию, если есть две процедуры, каждая из которых может начинать изменение?


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#72 28.04.2010 22:45:10

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Разные вопросы, в основном по процедурам

Блин, со вторым разобрался...
Теперь понятно почему вы говорили о точках сохранения.
Сделал так:

Код:

CREATE
DEFINER = 'root'@'%'
PROCEDURE StartContractTransaction()
SQL SECURITY INVOKER
COMMENT 'Начинает и настраивает транзакцию, если она ещё не была начата.'
sproc: BEGIN
  DECLARE CONTINUE HANDLER FOR SQLSTATE '42000'
    BEGIN
      set transaction isolation level read uncommitted;
      start transaction;
    END;
  release savepoint __sct_spoint;
  savepoint __sct_spoint;
END;

Отредактированно Артём Н. (28.04.2010 22:48:23)


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#73 28.04.2010 23:24:24

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Разные вопросы, в основном по процедурам

И насчёт хэндлера, обрабатывающего дублирование ключа.
Нужно делать примерно так?

Код:

CREATE
DEFINER = 'root'@'%'
PROCEDURE CarAdd(
  IN v_arenda BOOL,
  IN v_id_purpose_type INT,
  IN v_id_car_type INT,
  IN v_id_client VARCHAR(22),
  IN v_id_car_mark INT,
  IN v_car_model VARCHAR(255),
  IN v_vin_num VARCHAR(255),
  IN v_year_issue INT,
  IN v_power_kvt FLOAT,
  IN v_power_ls FLOAT,
  IN v_max_kg FLOAT,
  IN v_num_places INT,
  IN v_shassi VARCHAR(255),
  IN v_kusov VARCHAR(255),
  IN v_gos_num VARCHAR(255),
  IN v_pts_date DATE,
  IN v_pts_ser VARCHAR(255),
  IN v_pts_no VARCHAR(255),
  IN v_foreing BOOL,
  IN v_comments TEXT,
  OUT v_id_car DECIMAL(23, 0)
)
SQL SECURITY DEFINER
COMMENT 'Предположительные привилегии: пользователь.'
sproc: BEGIN
  declare granted BOOL;
  declare car_id DECIMAL(23, 0);
  # Дублирование ключа.
  DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET car_id := car_id + 1;

  # Проверяю полномочия.
  select ifnull(PRIV_CAR_ADD = true, false) from user_groups
    where ID_GROUP = GetGroupID() into granted;
  if (not granted) then
    call SetLastError('ERR_NOT_ENOUGH_RIGHTS');
    leave sproc;
  end if;

  select ifnull(max(ID_CAR) + 1, 0) into car_id from car;

  repeat
    insert into car(ID_CAR, CAR_MARK, CAR_MODEL, VIN_NUM, YEAR_ISSUE, POWER_KVT,
      POWER_LS, MAX_KG, NUM_PLACES, SHASSI, KUSOV, GOS_NUM, PTS_SER, PTS_NO,
      PTS_DATE, ARENDA, ID_PURPOSE_TYPE, ID_CAR_TYPE, ID_CLIENT, FOREING,
      COMMENTS, DATE_INSERT, USER_UPDATE_NAME)
    values(car_id, v_id_car_mark, v_car_model, v_vin_num, v_year_issue,
      v_power_kvt, v_power_ls, v_max_kg, v_num_places, v_shassi, v_kusov,
      v_gos_num, v_pts_ser, v_pts_no, v_pts_date, v_arenda, v_id_purpose_type,
      v_id_car_type, v_id_client, v_foreing, v_comments,
      CURDATE(), GetCurUserFullname());
    # Проверяю было ли вставлено ТС.
    select ROW_COUNT() >= 1 into granted;
  until (granted)
  end repeat;
  set v_id_car := car_id;
  call SetLastError('ERR_NO_ERROR');
END;

Вначале, я без repeat сделал. Может, где-то ещё серьёзно накосячил?

Отредактированно Артём Н. (28.04.2010 23:25:23)


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

Board footer

Работает на PunBB
© Copyright 2002–2008 Rickard Andersson