Предотвращение двойного бронирования - PullRequest
4 голосов
/ 21 октября 2011

Скажем, если у меня есть следующее:

create table jobs
(
  staff_id number,
  job_name varchar2(1000) not null,
  start_date date not null,
  end_date date not null
);

Это просто таблица с указанием различных рабочих мест персонала.

Теперь сотрудники могут выполнять только одну работу за раз, поэтому я вставляю задания, используя следующую инструкцию PL / SQL. Если найдено конфликтующее задание, оно не добавляется (вероятно, я должен сообщить об ошибке здесь, но для этого упрощенного примера я игнорирую это):

create or replace procedure add_job
(
  p_staff_id number, 
  p_job_name varchar2, 
  p_start_date date, 
  p_end_date date
) 
as
begin
  insert into jobs 
  (
    select p_staff_id, p_job_name, p_start_date, p_end_date from dual 
    where not exists
    (
      select 1 from jobs 
      where staff_id = p_staff_id
        and (end_date > p_start_date)
        and (start_date < p_end_date)
    )
  );
end;

Проблема в том, что, если я добавлю две разные работы в две разные сессии, а затем зафиксирую, я могу удвоить количество заказов. то есть следующее:

-- Session 1:
add_job(1, 'Help Alice', to_date('2011/08/21 11:00:00', 'YYYY/MM/DD HH24/MI/SS'), to_date('2011/08/21 13:00:00', 'YYYY/MM/DD HH24/MI/SS'));

-- Session 2:
add_job(1, 'Help Bob',   to_date('2011/08/21 12:00:00', 'YYYY/MM/DD HH24/MI/SS'), to_date('2011/08/21 14:00:00', 'YYYY/MM/DD HH24/MI/SS'));

-- Session 1:
commit;

-- Session 2:
commit;

Выше, staff_id 1 будет забронирован дважды с 12:00 до 13:00.

Похоже, что добавление в начало моей процедуры:

lock table jobs in exclusive mode;

сделал трюк, я чувствую, что это слишком широкий замок. Есть ли способ заставить оракула сделать что-то более тонкое. Я бы предпочел не копаться с dbms_lock, если это возможно. Эта страница намекнула select ... for update, но это не дало подробностей.

Можно ли предотвратить двойное бронирование без полной блокировки таблицы или dbms_lock блокировки? (Я использую Oracle 10g, если это имеет значение).

Ответы [ 5 ]

2 голосов
/ 21 октября 2011

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

select id from staff where id = p_staff_id for update;

Таким образом, блокировка влияет только на одного сотрудника (если у вас есть блокировки на уровне строк).

0 голосов
/ 21 октября 2011

До тех пор, пока задания распределяются в достаточно большие промежутки времени, 1 час или 30 минут (1/24/60 * 30)

Вы можете создать новую таблицу booked_time_slot с первичным ключом на staff_id & time (или дата и номер слота)

Если вы вставляете строку для каждого используемого временного интервала: (приведенный ниже пример получает каждые 30 минут между начальным и конечным временем и сохраняет их)

INSERT INTO booked_time_slot
  SELECT p_staff_id,TO_DATE('2011/08/21 11:00:00', 'YYYY/MM/DD HH24/MI/SS') + 
      ((ROWNUM-1)*(1/24/60*30))  
  FROM DUAL
  CONNECT BY TO_DATE('2011/08/21 11:00:00', 'YYYY/MM/DD HH24/MI/SS') + 
      ((ROWNUM-1)*(1/24/60*30)) <  
          TO_DATE('2011/08/21 12:00:00', 'YYYY/MM/DD HH24/MI/SS')

Первичный ключ остановит любые дубликаты.

0 голосов
/ 21 октября 2011

Один из способов, который вы могли бы рассмотреть, - это использование материализованных представлений с проверочными ограничениями.См. мой старый пост в блоге , где приведен пример «1) Правило: у сотрудника не может быть перекрывающихся назначений проекта», очень похожего на ваше.так что не выполняйте без надлежащего тестирования производительности.

0 голосов
/ 21 октября 2011

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

Если найдено перекрывающееся задание для отдельного сотрудника, оно не разрешит вставку, и поэтому выне удалось получить перекрывающиеся задания.

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

Кстати, блокировка таблицы ENTIRE не будетхорошее решениеКроме того, попытка использовать SELECT FOR UPDATE не помешает двум вставкам.

0 голосов
/ 21 октября 2011

если staff_id должен быть уникальным, вы должны генерировать его по порядку.

и когда вы делаете вставку, вы просто делаете это следующим образом:

insert into jobs 
(
    select seq_staff_id.nextval, p_job_name, p_start_date, p_end_date from dual 
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...