简体   繁体   English

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

[英]Help with SQL query to find next available date for a reservation system

I am attempting to write an SQL query for a reservations system to find the earliest available rental date for an item. 我正在尝试为预订系统编写SQL查询,以查找某物品的最早可用租赁日期。

Items are categorized in the database by SKU. 项目通过SKU在数据库中分类。 There can be multiple copies of an item, each uniquely identified in the database by its serial number. 一个项目可以有多个副本,每个副本在数据库中均通过序列号唯一标识。

When searching for the earliest available rental date for an item, it doesn't matter which serial number is chosen; 在搜索项目的最早可用租赁日期时,选择哪个序列号都无关紧要; simply the next one available. 只是下一个可用。

The database has 2 tables; 该数据库有2个表。 "Reservations" and "Items". “预订”和“项目”。 There is also a Calendar table of several thousand YYYY-MM-DD future dates to work with. 还有一个日历表,其中包含数千个YYYY-MM-DD将来的日期。

The Reservations table contains columns; Reservations表包含列; "record_number","sku", "serial_number", "start_date", "end_date" plus customer data. “ record_number”,“ sku”,“ serial_number”,“ start_date”,“ end_date”以及客户数据。 This is where each reservation is recorded as it is made. 在此记录每个预约的位置。

The Items table contains columns; Items表包含列; "sku" and "serial_number". “ sku”和“ serial_number”。 This is an inventory of all rental items in the system. 这是系统中所有租赁项目的清单。

I've worked the problem for over 2 days, but my SQL knowledge isn't enough to solve this puzzle. 我已经解决了超过2天的问题,但是我的SQL知识不足以解决这个难题。

I've progressed as far as generating a list of dates that have at least one reservation for a particular SKU: 到目前为止,我已经生成了一个日期列表,该日期列表至少具有一个特定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'

I can sub-query the above into a "NOT IN ..." select statement but that only accomplishes finding dates having NO reservations for a particular SKU. 我可以将上述内容子查询到“ NOT IN ...”选择语句中,但这只能完成查找没有特定SKU保留日期的日期。 I need to find the first date where at least one item is available. 我需要找到至少一个项目可用的第一个日期。

I have imagined joining the dates of the Calendar table with the SKUs from the Items table with the reservation numbers of the Reservation table looking for "NULL" in the reservation_record, indicating no reservation exists for that date and serial-number combination. 我曾想过将Calendar表的日期与Items表中的SKU结合在一起,其中Reservation表的保留号在Reservation_record中寻找“ NULL”,指示该日期和序列号组合不存在任何保留。 But I have been unable to write such a query that works. 但是我一直无法编写这样的查询。

Questions are welcome. 欢迎提问。

The following should get you going. 以下内容将助您一臂之力。 you may want to adjust my sample of "Current_Date()" function for whatever may be your reservation start date and going out so many days.... 您可能想要调整我的“ Current_Date()”函数示例,以适应您的预订开始日期和出库时间如此之长。...

This uses MySQL inline variables in the query. 这在查询中使用MySQL内联变量。 The inner query is a simple prepare of a reservation (@r) variable based on some starting date ( current_date() ), and joins to the item table. 内部查询是基于某个开始日期(current_date())的保留(@r)变量的简单准备,并连接到项目表。 By doing no join clause, it would otherwise grab one date for every item. 通过不执行join子句,否则它将为每个项目获取一个日期。 In my scenario, I'm only considering going out 30 days, so I've applied a limit of the first 30 items. 在我的情况下,我只考虑过30天,因此我对前30个项目应用了限制。 No basis other than give me enough records so I don't have to create a temp table of 30 records (or however many days you want to go out). 除了给我足够的记录外,没有其他依据,因此我不必创建包含30条记录的临时表(或者您想出去的天数)。 This creates an aliased query "JustDates" and has a single column "OpenDate". 这将创建一个别名查询“ JustDates”,并具有一个列“ OpenDate”。 This is the basis of date ranges to test for. 这是要测试的日期范围的基础。

This is now joined to the items table, but no condition creates a Cartesian to say for each date, compare with every item... per the WHERE clause, I am only concerned with items having SKU of "ABC123" weather they have 10 serial #s or 100. This would now give me a possible 300 or 3000 (10 serial items @ 30 days, or 100 serial items @ 30 days. 现在已将其连接到项目表,但是没有条件会创建笛卡尔表示每个日期,并与每个项目进行比较...根据WHERE子句,我只关心SKU为“ ABC123”的项目,因为它们的序列号为10 #s或100。现在,我可能得到300或3000(30天为10个序列项,或30天为100个序列项。

Now that I have a "range" of all individual serial numbers and possible days to check availability, I can now query against the reservations system. 现在,我有了所有单个序列号的“范围”和可能的可用天数,现在可以查询预订系统了。 So, via a sub-select, and NOT IN for a given matching SKU, SERIAL #, and the POSSIBLE Date being found in reservations, I only want to keep those where the given OpenDate is NOT found. 因此,通过子选择,对于给定的匹配SKU,SERIAL#和在预订中找到可能的日期的NOT IN,我只想保留那些未找到给定OpenDate的商品。 I've simulated your table structures and put in a handful of items, multiple serial numbers and staggared reservation date ranges and it works great... 我已经模拟了您的表格结构,并放入了一些项目,多个序列号和滞留的预订日期范围,并且效果很好...

Obviously, I would ensure indexes on sku / serial for performance. 显然,我将确保sku / serial上的索引性能。 The only additional change I might make is when querying against the reservations, to exclude any reservations where the end date is prior to the starting date in question for YOUR query, and optionally, no Start Date > the LAST date you are considering. 我唯一可能做的其他更改是在查询保留时,以排除结束日期早于您要查询的开始日期的任何保留,并且可以选择不包括开始日期>您正在考虑的最后日期。 If you have a ton of reservations spanning years, who cares about something ancient, or something way in the future from the date range in question. 如果您有很多跨越多年的保留意见,那么谁会在乎有关日期范围的某些古老的东西或将来的某种方式。

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

Unfortunately, you can't look for missing rows (you can for rows that don't exist, but it's not the same). 不幸的是,您无法查找丢失的行(可以查找不存在的行,但是不一样)。

You had a good start, querying the calendar table and left joining to the reservations, but you made a mistake by putting the filtering condition in the WHERE clause where it will return no rows instead of in the join ON clause. 您有一个很好的开始,查询日历表并保留对保留的联接,但是通过将过滤条件放在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

This query looks for the earliest date in the calendar table where the existing number of items in the items table is more than the number of reservations for those dates (ie, first date where you have stock available). 此查询在日历表中查找最早的日期,其中项目表中的现有项目数大于这些日期的预订数(即,您有库存的第一个日期)。

( EDIT : Substitute 'id' with the primary column in those tables) 编辑 :用这些表中的主列替换“ id”)

I learned a really valuable lesson from a SQL expert a long time ago. 很久以前,我从SQL专家那里学到了非常有价值的一课。

Don't let what you have blind you to what you need . 不要让你拥有的东西蒙蔽你的需求 Think about where reservations are used in the real world. 考虑一下在现实世界中使用预订的地方。

  • Hotels 酒店
  • Airlines 航空公司
  • Cruise ships 游轮
  • Arenas 阿里纳斯
  • Football stadiums 足球场

Every one of these has a fixed inventory of seats on a particular day. 这些中的每一个在特定日期都有固定的座位数。 Each of those seats might be either open or reserved--tied to a specific customer. 这些座位中的每一个都可以是开放的或预留的-与特定客户绑定。

You have a fixed inventory of items on a particular day. 您在特定日期有固定的项目清单。 Each of those items might be either available or reserved--tied to a specific customer. 这些项目中的每一个都可能是可用的或保留的-与特定客户绑定的。

I think your life would be a lot easier if you created a table of availability rather than a table of reservations. 我认为,如果您创建一个可用性表而不是预订表,您的生活将会轻松很多。

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);

(For production, I'd build a stored procedure to populate the availability table.) (对于生产,我将构建一个存储过程来填充可用性表。)

Now it's just dead simple to select the earliest available date for a given sku. 现在,选择给定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

The request start and end dates are the periods of time over which someone wants to reserve the given item. 请求的开始日期和结束日期是某人要保留给定项目的时间段。 (Eg, "I want to reserve the widget sometime between July and September") (例如,“我想在7月到9月之间的某个时间保留小部件”)

Revised to handle existing stock on-hand. 修改以处理现有的现有库存。 Although, it isn't clear how that is actually stored. 虽然,目前尚不清楚该如何实际存储。 Do you have an on-hand value for each SKU or is each item given its own row? 对于每个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)
                )

If your items table actually stores an on-hand value for the number of a given SKU in stock, then yo can simply change the query like so: 如果您的项目表实际上存储了库存中给定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)
                )

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM