Как проверить, забронирован ли номер в отеле, используя ограничения Oracle SQL - PullRequest
1 голос
/ 08 апреля 2020

Я абсолютный новичок, когда дело доходит до SQL, и я пытаюсь выяснить, как сделать простую проверку целостности. Я моделирую систему регистрации отелей с четырьмя столами: Отель, Номер, Бронирование и Гость. Меня интересует таблица бронирования с атрибутами hotelNo, guestNo, dateFrom, dateTo, roomNo, где первые три являются составным первичным ключом. Теперь проблема заключается в том, что при существующей системе два человека могут быть забронированы в одно и то же время одновременно, что, очевидно, будет проблемой в реальной жизни. Я полагаю, что решение может начать выглядеть примерно так:

CREATE TABLE Booking(
-- All the attribute definitions go here...
    CONSTRAINT OneGuestAtATime
        CHECK (NOT EXISTS(SELECT(dateFrom FROM Booking ...))) -- I become unsure of what to do around here
);

Имейте в виду, что, хотя я учусь на компьютерного инженера, я никогда не делал SQL раньше, так что ручная работа будет признателен:)

Редактировать: я думаю, что моя проблема будет решена с помощью ограничения на

Случай 1 (Перекрытие): «Если атрибуты dateFrom или dateTo записи I я пытаюсь вставить падение между атрибутами dateFrom и dateTo данной записи, ранее находившейся в таблице, отклонить эту вставку, поскольку между этими двумя бронированиями есть некоторое перекрытие. "

CASE 2 (Superset):" Если я при попытке вставить запись X и в таблице уже есть запись с именем Y, так что ее Y.dateFrom> X.dateFrom и Y.dateTo

Я не уверен, как это перевести на SQL.

Редактировать 2: Таблицы

CREATE TABLE Hotel (
    hotelNo NUMBER NOT NULL,
    hotelName VARCHAR2(1024) NOT NULL,
    city VARCHAR2(1024) NOT NULL,
    --
    PRIMARY KEY (hotelNo)
);

CREATE TABLE Room (
    roomNo NUMBER(4,0) NOT NULL,
    hotelNo NUMBER(5,0) NOT NULL,
    type VARCHAR2(1024),
    price NUMBER(6,2) NOT NULL,
    --
    PRIMARY KEY (roomNo, hotelNo),
    FOREIGN KEY (hotelNo) REFERENCES Hotel
);

CREATE TABLE Guest(
    guestNo NUMBER(8,0) NOT NULL,
    guestName VARCHAR(1024) NOT NULL,
    guestAddress VARCHAR(1024) NOT NULL,
    --
    PRIMARY KEY (guestNo)
);

CREATE TABLE Booking(
    hotelNo NUMBER(8,0) NOT NULL,
    guestNo NUMBER(8,0) NOT NULL,
    dateFrom DATE NOT NULL,
    dateTo DATE NOT NULL,
    roomNo NUMBER(4,0) NOT NULL,
    --
    PRIMARY KEY (hotelNo, guestNo, dateFrom),
    FOREIGN KEY (hotelNo) REFERENCES Hotel,
    FOREIGN KEY (guestNo) REFERENCES Guest,
    FOREIGN KEY (hotelNo, roomNo) REFERENCES Room(hotelNo, roomNo),
    --
    CONSTRAINT DateIntegrity
        CHECK (dateFrom < dateTo)

1 Ответ

1 голос
/ 08 апреля 2020

В вашей модели с таблицами HOTEL и GUEST все в порядке (им может понадобиться еще несколько столбцов позже, но это не проблема). Для ROOM вы решили использовать композитный ПК. Однако достаточно одного столбца в качестве идентификатора. При бронировании внешний ключ, ссылающийся на HOTEL, является избыточным. Гости бронируют номера (которые имеют уникальный идентификатор и уже привязаны к ОТЕЛЬУ) в определенные дни.

Может помочь (ваше обучение) автоматически сгенерировать идентификаторы и определить различные «начальные» значения для них - при запросе таблиц на более позднем этапе вы сразу узнаете идентификаторы, например, в ОТЕЛЯХ может быть более 1000, в номерах может быть более 2000 и более c (см. код DDL ниже).

При выполнении ссылка предоставлена ​​@Abra, вы видели, что для решения проблемы могут использоваться триггеры и т. д. c. Приведенное ниже решение вдохновлено этим ответом (также упоминаемым как «вариант 4» здесь ) и использует идею разбивки бронирований на дни («слоты»), которые затем могут использоваться для уникальных (или PK) ограничений. Пожалуйста, прочитайте комментарии, так как они содержат больше объяснений.

Код DDL

create table hotels (
  id number generated always as identity start with 1000 primary key
, name_ varchar2( 100 )
) ;

create table rooms (
  id number generated always as identity start with 2000 primary key
, name_ varchar2( 100 )
, hotelid number references hotels( id )
) ;

create table guests (
  id number generated always as identity start with 3000 primary key
, last_name varchar2( 100 )
) ;

-- additional table, populated 500 days "into the future"
-- (no bookings _before_ the sysdate) due to FK constraint in bookings
create table days ( slot primary key )
as
select trunc( sysdate ) + level
from dual
connect by level <= 500 ;  -- Oracle only!

create table bookings (
  roomid number references rooms( id )
, slot date references days( slot ) not null
, guestid number references guests( id ) not null
, constraint bookings_pk primary key( roomid, slot )
) ;

Модель

enter image description here

Заполните ГОСТИНИЦЫ, НОМЕРА, ГОСТИ

-- For populating HOTELS, ROOMS, and GUESTS, we are just using a little PL/SQL script.
-- You can also use single INSERTs.
begin
-- insert one hotel  
  insert into hotels ( name_ ) values ( 'Tiny Hotel' ) ;
-- insert 8 rooms  
  for r in 1 .. 8
  loop
    insert into rooms( name_, hotelid ) values ( 'room_' || to_char( r ), 1000 ) ;
  end loop ;
-- insert 9 guests
  for g in 1 .. 9
  loop
    insert into guests( last_name ) values ( 'guest_' || to_char( g ) ) ;
  end loop ;
  commit ;
end ;
/

Данные в ГОСТИНИЦАХ, НОМЕРАХ, ГОСТИ

SQL> select * from hotels ;
    ID NAME_        
  1000 Tiny Hotel   

SQL> select * from rooms ;
    ID NAME_      HOTELID 
  2000 room_1        1000 
  2001 room_2        1000 
  2002 room_3        1000 
  2003 room_4        1000 
  2004 room_5        1000 
  2005 room_6        1000 
  2006 room_7        1000 
  2007 room_8        1000 

SQL> select * from guests ;
    ID LAST_NAME   
  3000 guest_1     
  3001 guest_2     
  3002 guest_3     
  3003 guest_4     
  3004 guest_5     
  3005 guest_6     
  3006 guest_7     
  3007 guest_8     
  3008 guest_9 

Тестирование

-- tests for bookings - unique (roomid, slot)
-- guest 3000 books room 2000, 2 days
-- these 2 inserts must succeed
insert into bookings ( roomid, guestid, slot ) 
  values ( 2000, 3000, date '2020-10-10' ) ;
insert into bookings ( roomid, guestid, slot ) 
  values ( 2000, 3000, date '2020-10-10' + 1 ) ;  -- + 1 here could be + i in a loop ...

-- INSERT must fail - guest 3000 cannot book room 2000 twice (on the same day)
insert into bookings ( roomid, guestid, slot ) 
  values ( 2000, 3000, date '2020-10-10' ) ;
--ERROR at line 1:
--ORA-00001: unique constraint (...BOOKINGS_PK) violated


-- this INSERT must fail
-- guest 3001 cannot have room 2000 on the same day as guest 3000
insert into bookings ( roomid, guestid, slot ) 
  values ( 2000, 3001, date '2020-10-10' + 1 ) ;
--ERROR at line 1:
--ORA-00001: unique constraint (...BOOKINGS_PK) violated


-- guest 3001 can have a different room at the same date, though
-- this insert must succeed
insert into bookings ( roomid, guestid, slot ) 
  values ( 2001, 3001, date '2020-10-10' + 1 ) ;
-- 1 row created.

Вы можете вставить больше дат для тестирования, используя PL / SQL и кодировку al oop: см. dbfiddle .

Простой query

-- all current bookings
select
  H.name_
, R.name_
, G.last_name
, B.slot
from hotels H
  join rooms R on H.id = R.hotelid
  join bookings B on R.id = B.roomid
  join guests G on G.id = B.guestid
;

-- result
NAME_        NAME_    LAST_NAME   SLOT        
Tiny Hotel   room_1   guest_1     10-OCT-20   
Tiny Hotel   room_1   guest_1     11-OCT-20   
Tiny Hotel   room_1   guest_4     01-DEC-20   
Tiny Hotel   room_1   guest_4     02-DEC-20   
Tiny Hotel   room_1   guest_4     03-DEC-20  
...

Запрос с большим количеством тестовых данных (INSERT: смотрите dbfiddle)

-- query that returns
-- all current bookings with nights_booked etc
-- CAUTION: for recurring bookings (same ROOM and GUEST but different slots)
--   this query will give us misleading results
select
  H.name_
, R.name_
, G.last_name
, count( B.slot)     nights_booked
, min( B.slot )      arrival_date
, max( B.slot ) + 1  departure_date
from hotels H
  join rooms R on H.id = R.hotelid
  join bookings B on R.id = B.roomid
  join guests G on G.id = B.guestid
group by H.name_, R.name_, G.last_name
;

-- result
NAME_        NAME_    LAST_NAME     NIGHTS_BOOKED ARRIVAL_DATE   DEPARTURE_DATE   
Tiny Hotel   room_1   guest_1                   2 10-OCT-20      12-OCT-20        
Tiny Hotel   room_1   guest_4                  21 01-DEC-20      22-DEC-20        
Tiny Hotel   room_2   guest_2                   1 11-OCT-20      12-OCT-20 

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

...