我正在尝试为预订系统编写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”,表示该日期和序列号组合没有预留。但我一直无法编写这样有效的查询。
欢迎提出问题。
答案 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)
)