帮助SQL查询以查找预订系统的下一个可用日期

时间:2011-02-26 01:10:39

标签: mysql sql

我正在尝试为预订系统编写SQL查询,以查找项目的最早可用租赁日期。

SKU将数据分类到数据库中。可以有一个项目的多个副本,每个副本在数据库中通过其序列号唯一标识。

在搜索项目的最早可用租赁日期时,选择哪个序列号并不重要;只是下一个可用。

数据库有2个表; “预订”和“项目”。还有一个包含数千个YYYY-MM-DD未来日期的日历表。

Reservations表包含列; “record_number”,“sku”,“serial_number”,“start_date”,“end_date”加上客户数据。这是每个预订记录的地方。

Items表包含列; “sku”和“serial_number”。这是系统中所有租赁项目的清单。

我已经解决了2天以上的问题,但我的SQL知识还不足以解决这个问题。

我已经取得了进展,生成了至少有一个特定SKU预订的日期列表:

   SELECT calendar.dt
     FROM calendar
LEFT JOIN reservations ON calendar.dt >= reservations.start_date 
                      AND calendar.dt <= reservations.end_date
    WHERE reservations.sku = 'ABC123'

我可以将上面的查询子查询到“NOT IN ...”选择语句,但这只能完成查找对特定SKU没有保留的日期。我需要找到至少有一个项目可用的第一个日期。

我想象使用Items表中的SKU加入Calendar表的日期,Reservation表的预留编号在reservation_record中查找“NULL”,表示该日期和序列号组合没有预留。但我一直无法编写这样有效的查询。

欢迎提出问题。

4 个答案:

答案 0 :(得分:2)

以下内容应该让你前进。您可能想调整我的“Current_Date()”函数样本,无论您的预订开始日期是多少天,都可以出去......

这在查询中使用MySQL内联变量。内部查询是基于某个开始日期(current_date())的预留(@r)变量的简单准备,并且连接到项目表。通过不执行join子句,它将为每个项目获取一个日期。在我的场景中,我只考虑外出30天,所以我已经应用了前30项的限制。除了给我足够的记录之外没有基础,所以我不必创建一个包含30条记录的临时表(或者你想要出去多少天)。这将创建别名查询“JustDates”并具有单个列“OpenDate”。这是测试日期范围的基础。

现在已经加入了items表,但没有条件为每个日期创建一个笛卡尔说,与每个项目进行比较...根据WHERE子句,我只关心SKU为“ABC123”天气的项目有10个序列号或100个。现在可以给我300或3000(10天30天串联项目,或30天30个连续项目。

现在我有一个所有单独序列号的“范围”以及检查可用性的可能天数,现在我可以查询预订系统。因此,通过子选择和NOT IN来获得给定的匹配SKU,SERIAL#和在预订中找到的可能日期,我只想保留那些找不到给定OpenDate的日期。我已经模拟了你的桌子结构并放入了一些项目,多个序列号和滞留的预订日期范围,它很有效......

显然,我会确保sku / serial上的索引的性能。我可能做的唯一额外更改是在查询预订时,排除任何结束日期在您的查询的开始日期之前的预订,并且可选地,没有开始日期&gt;你正在考虑的最后一个日期。如果您有多年的保留意见,那么他们会关注某些古老的东西,或者从未来的某个方面开始。

select  items.sku,
        items.serial_number,
        JustDates.OpenDate
    from 
        ( SELECT 
                 @r:= date_add(@r, interval 1 day ) OpenDate
            FROM 
                 (select @r := current_date()) vars,
                items limit 30 ) JustDates,
        items
    where 
            sku = "ABC123"
        and sku not in ( select sku from Reservations
                            where items.sku = reservations.sku
                              and items.serial_number = reservations.serial_number
                              and justDates.OpenDate >= reservations.start_date
                              and justDates.OpenDate <= reservations.end_date )
    order by 
       items.serial_number,
       justDates.OpenDate

答案 1 :(得分:1)

不幸的是,您无法查找缺失的行(您可以查找不存在的行,但它们不相同)。

您有一个良好的开端,查询日历表并离开加入预订,但是您在WHERE子句中将过滤条件放在其中将不返回任何行而不是在join ON子句中时犯了一个错误。

SELECT calendar.dt,
   COUNT(DISTINCT reservations.id) AS reserved,
   COUNT(DISTINCT items.id) AS inventory
FROM calendar
LEFT JOIN reservations
   ON calendar.dt >= reservations.start_date 
   AND calendar.dt <= reservations.end_date
   AND reservations.sku = 'ABC123'
LEFT JOIN items ON items.sku=reservations.sku
GROUP BY calendar.dt
HAVING inventory > reserved OR reserved = 0
ORDER BY calendar.dt ASC 
LIMIT 1

此查询在日历表中查找最早的日期,其中项目表中的现有项目数量大于这些日期的预订数量(即,您有库存的第一个日期)。

编辑:用这些表中的主列替换'id')

答案 2 :(得分:1)

很久以前,我从SQL专家那里学到了非常宝贵的一课。

不要让你对你所需要的东西视而不见。想想在现实世界中使用预订的地方。

  • 酒店
  • 游轮
  • 竞技场
  • 足球场

其中每一个都在特定日期有固定的座位库存。这些席位中的每一个都可能是开放的或保留的 - 与特定客户相关联。

您在特定日期拥有固定的商品库存。这些项目中的每一项都可能是可用的或保留的 - 与特定客户相关联。

我认为如果你创建一个可用性表而不是预订表,你的生活将会轻松得多。

create table availability (
  sku varchar(15) not null,
  sn varchar(15) not null,
  av_date date not null,
  customer_id integer, --references customers (customer_id)
  primary key (sku, sn, av_date),
  foreign key (sku, sn) references items (sku, sn)
);

insert into availability values
('1', '1', '2011-01-01', 1),  -- reserved by customer_id 1
('1', '1', '2011-01-02', 1),
('1', '1', '2011-01-03', 1),
('1', '1', '2011-01-04', NULL), -- not yet reserved
('1', '1', '2011-01-05', NULL),
('1', '1', '2011-01-06', NULL),
('1', '1', '2011-01-07', NULL),
('1', '1', '2011-01-08', NULL),
('1', '1', '2011-01-09', NULL),
('1', '1', '2011-01-10', NULL),
('1', '2', '2011-01-01', NULL),
('1', '2', '2011-01-02', 2),
('1', '2', '2011-01-03', 2),
('1', '2', '2011-01-04', 3),
('1', '2', '2011-01-05', 3),
('1', '2', '2011-01-06', NULL),
('1', '2', '2011-01-07', NULL),
('1', '2', '2011-01-08', NULL),
('1', '2', '2011-01-09', NULL),
('1', '2', '2011-01-10', NULL);

(对于生产,我将构建一个存储过程来填充可用性表。)

现在,选择给定sku的最早可用日期非常简单。

select sku, sn, av_date, customer_id 
from availability 
where sku = '1' and customer_id is null
order by av_date asc
limit 1

答案 3 :(得分:0)

请求开始日期和结束日期是某人想要保留给定项目的时间段。 (例如,“我想在7月到9月之间的某个时间保留小部件”)

修改以处理现有库存。虽然,目前尚不清楚它是如何实际存储的。您是否拥有每个SKU的现有价值,或者每个项目都有自己的行?

Select Min( C.dt ) As EarliestAvailableDate
From Calendar As C
    Left Join Reservations As R
        On R.StartDate <= C.dt
            And R.EndDate >= C.dt
            And R.Sku = 'ABC123'
Where C.dt Between '[Request Start Date]' And '[Request End Date]'
    And R.record_number Is Null
    And Exists  (
                Select 1
                From Items As I1
                    Left Join Reservations As R1
                        On R1.StartDate <= C.dt
                            And R1.EndDate >= C.dt
                            And R1.Sku = I1.SKU
                Where I.SKU = 'ABC123'
                Having Count(*) > Count(R1.record_number)
                )

如果您的商品表实际存储了库存中给定SKU数量的现有价值,那么您可以像这样更改查询:

Select Min( C.dt ) As EarliestAvailableDate
From Calendar As C
    Left Join Reservations As R
        On R.StartDate <= C.dt
            And R.EndDate >= C.dt
            And R.Sku = 'ABC123'
Where C.dt Between '[Request Start Date]' And '[Request End Date]'
    And R.record_number Is Null
    And Exists  (
                Select 1
                From Items As I1
                    Left Join Reservations As R1
                        On R1.StartDate <= C.dt
                            And R1.EndDate >= C.dt
                            And R1.Sku = I1.SKU
                Where I.SKU = 'ABC123'
                Having I1.OnHandCount > Count(R1.record_number)
                )