zoukankan      html  css  js  c++  java
  • SQL puzzles and answers读书笔记——酒店订房问题

    问题描述

    Hotel表记录了酒店订房的信息,创建Hotel表的脚本如下:

    create table Hotel
    (
      room_nbr int not null,
      arrival_date date not null,
      departure_date date not null,
      guest_name char(30) not null,
      primary key(room_nbr, arrival_date),
      check(departure_date >= arrival_date)
    );

    其中room_nbr为房间号,arrival_date为入住日期,departure_date为退房日期,guest_name为住客名。

    对于这张表已经加了主键约束primary key(room_nbr, arrival_date)和check约束departure_date >= arrival_date

    但这些还不够,我们的问题是:如何防止同一个房间预订的时间产生重叠。

    解决方案

    方案1

    首先我们可以试图使用check约束来解决这个问题,尝试写如下script:

    create table Hotel
    (
      room_nbr int not null,
      arrival_date date not null,
      departure_date date not null,
      guest_name char(30) not null,
      primary key(room_nbr, arrival_date),
      check(departure_date >= arrival_date),
      check(not exists
              (select *
              from Hotel H1 inner join Hotel H2
              on H1.room_nbr = H2.room_nbr
                and H1.arrival_date between H2.arrival_date and H2.departure_date
              )  
          )
    );

    很可惜在Oracle 11g和SQL Server 2008中这种写法都无法通过语法,出错提示大致为:在check中不能写子查询。

    同时这里check中的逻辑本身也存在问题,就算语法支持,也可能无法插入任何数据(方案3中会进一步涉及此问题)。

    方案2

    重新设计表结构,为每一个房间的每一天预订对应一条记录,script如下:

    create table Hotel
    (
      room_nbr int not null,
      occupy_date date not null,
      guest_name char(30) not null,
      primary key (room_nbr, occupy_date)
    );

    这样设计解决了重叠预订的问题,但会带来数据的冗余,而且插入预订记录也变得麻烦了。

    方案3

    创建带with check option的View,之后通过View来操作表,script如下:

    CREATE OR REPLACE VIEW HOTELSTAYS
    AS 
      select
      H1.room_nbr,
      H1.ARRIVAL_DATE,
      H1.DEPARTURE_DATE,
      H1.GUEST_NAME
    from
      Hotel H1
    where
      not exists
      (select
        *
      from
        Hotel H2
      where
        H1.room_nbr = H2.room_nbr
        and
        H1.ARRIVAL_DATE between H2.arrival_date and H2.departure_date  
      )
    with check option;

    在创建此View之后,在Oracle 11g中向此View插入一条预订记录能成功,但再查看此View却无法查到刚插入的数据。如果在SQL Server 2008中,此View无法插入任何数据。

    造成这种情况的原因是此View子查询中的H1.room_nbr = H2.room_nbr and H1.ARRIVAL_DATE between H2.arrival_date and H2.departure_date一定会连接到自身的记录。

    我们可以在Hotel表中加入Id列,并给予unique约束,script如下:

    create table Hotel
    (
      reserve_id int not null unique,
      room_nbr int not null,
      arrival_date date not null,
      departure_date date not null,
      guest_name char(30) not null,
      primary key(room_nbr, arrival_date),
      check(departure_date >= arrival_date)
    );

    创建View的script相应做如下修改:

    CREATE OR REPLACE VIEW HOTELSTAYS
    AS 
      select
      H1.reserve_id,
      H1.room_nbr,
      H1.ARRIVAL_DATE,
      H1.DEPARTURE_DATE,
      H1.GUEST_NAME
    from
      Hotel H1
    where
      not exists
      (select
        *
      from
        Hotel H2
      where
        H1.room_nbr = H2.room_nbr
        and
        H1.ARRIVAL_DATE between H2.arrival_date and H2.departure_date
        and
        H1.reserve_id <> H2.reserve_id
      )
    with check option;

    这样问题就解决了。

    对于reserve_id在SQL Server中我们可以定义identity列,在Oracle中可以使用sequence进行插入。

  • 相关阅读:
    uva10129 PlayOnWords(并查集,欧拉回路)
    UVA439 knightMoves (A*启发搜索)
    uva297 Quadtrees (线段树思想,区间操作)
    [uva]AncientMessages象形文字识别 (dfs求连通块)
    [uva816]AbbottsRevenge Abbott的复仇(经典迷宫BFS)
    [dp][uestc]L
    [dp][uestc oj]J
    [uestc oj]H
    [dp uestc oj] G
    [dp]uestc oj 邱老师看电影
  • 原文地址:https://www.cnblogs.com/DBFocus/p/1817864.html
Copyright © 2011-2022 走看看