点击上方【蓝色】字体 关注我们


01 场景描述
这是在做一个大型货场租赁系统时遇到的问题,在计算货场剩余存储空间时,不仅仅需要知道哪些货位是空闲的,还要能够判断出哪些货位之间是连续的。因为在新货物入场时,可以判断这些货物是否可以堆放在一起,而不是放在不连续的多个货位上,这样更便于管理,并且在出货时也更加迅速。

相对于大型数据库而言,第一种架构设计对于查询语句编写方面会更方便一些。如果数据需要驻留在一些手持设备上,多数开发人员会更喜欢第二种架构设计,因为它能够节省宝贵的存储空间。尤其是当表的数据量非常大时,这种设计方式更能显示出它的优势。这里我们将以第二种架构设计方式来演示查询的创建方法,下面是创建示例表的语句。
create table freights asselect 1 id,1 numbfrom dualunion allselect 2 id, 2 numbfrom dualunion allselect 3 id,3 numbfrom dualunion allselect 4 id,4 numbfrom dualunion allselect 5 id,87 numbfrom dualunion allselect 6 id,89 numbfrom dualunion allselect 7 id,99 numbfrom dualunion allselect 8 id,100 numbfrom dual
问题2:查找已用货位区间?

02 剩余空位区间及剩余货位统计分析
要查找剩余空位区间,就是要找出表2所示的数值范围。

要找出这些区间的开始和结束编号,需要在间断之前的值加1,在下一组编号开始之间的值减1。例如,表中的5~86是在4的基础上加1、在87的基础上减1得来的。

可以看出,只要找出n2-n1大于1的货位组,并在n1上加1,在n2上减1,就可以得到表19-8所示的剩余空位区间。参考下面的SQL语句。
select id,n1 + 1 f_st,n2 - 1 f_etfrom (select id,numb n1,lead(numb) over (order by id) n2from FREIGHTS) twhere n2 - n1 > 1;

方法2:自关联形式实现
SELECT n1.Numb + 1 f_st,MIN(n2.Numb - 1) f_etFROM Freights n1JOIN Freights n2ON n2.Numb > n1.NumbGROUP BY n1.NumbHAVING (n1.Numb + 1) < MIN(n2.Numb);
WITH numbs(n) AS(SELECT 1 AS n from dualUNION ALLSELECT n + 1FROM numbsWHERE n < 100)SELECT nFROM numbs;
WITH numbs(n) AS(SELECT 1 AS nfrom dualUNION ALLSELECT n + 1FROM numbsWHERE n < 100)SELECT nFROM NumbsWHERE n NOT IN (SELECT Numb FROM Freights)
N86412876259031321633205762761607782652110799523736837855359881752634514978382243844454704958982462554253686980133911296634912615751851307292468356504864121993969474771677440358197
如果不需要返回全部的空货位号,而是几个的话,可以使用下面的查询语句。
SELECT numb, rn, (numb - rn) AS available_NumbFROM (SELECT Numb, ROW_NUMBER() OVER (ORDER BY numb) rnFROM Freights) tWHERE rn <> numb
NUMB RN AVAILABLE_NUMB87 5 8289 6 8399 7 92100 8 92
03 查找已用货位区间
现在已经使用的货位是1、2、3、4、87、89、99、100,已用货位区间即1~4、87~87、89~89、99~100。这些区间实际上是一组连续编号中的最小值和最大值,如1~4是货位1、2、3、4中的最小值和最大值。
select flg, min(n1) f_st, max(n1) f_edfrom (select id,n1,n2,--当状态一致时记为0,状态变化时记为1,相同状态的累计值不变,会持续状态开始前的值。sum(case when n1 - n2 <= 1 then 0 else 1 end) over (order by id) flgfrom (select id,numb n1,lag(numb) over (order by id) n2from FREIGHTS) t) tgroup by flgorder by flg
FLG F_ST F_ED1 1 42 87 873 89 894 99 100
04 小 结
问题拓展:如果货位的占用时间有重叠,该如何处理?
假设存在一个名为 warehouse
的表,其中包含space_id
(货位编号)、occupied_start_time
(占用开始时间)和occupied_end_time
(占用结束时间)这几个关键列来记录货位的占用情况。当然,实际情况中可能还有其他列,如货位大小、位置等信息,但对于查询未被占用的货位数量来说,上述三个列是重点关注的对象。数据示例:
| space_id | occupied_start_time | occupied_end_time |
| 1 | '2024-01-01 08:00:00' | '2024-01-02 17:00:00' |
| 2 | NULL | NULL |
| 3 | '2024-01-03 10:00:00' | '2024-01-03 14:00:00' |
识别重叠情况
- 时间区间表示法:将货位的占用时间看作一个时间区间,用起始时间()和结束时间()来表示,例如一个货位从时刻开始被占用,到时刻结束占用,那么这个时间区间可以表示为。
- 重叠的判定条件:当两个时间区间和满足以下条件之一时,就存在重叠。
(第二个区间的起始时间在第一个区间内) (第二个区间的结束时间在第一个区间内) (第一个区间的起始时间在第二个区间内) (第一个区间的结束时间在第二个区间内)
- 方法一:自连接和条件判断
- 思路
:通过将表与自身进行连接,比较每两个占用时间区间是否存在重叠。 - 查询示例
SELECT w1.space_id, w1.occupied_start_time, w1.occupied_end_time,w2.space_id, w2.occupied_start_time, w2.occupied_end_timeFROM warehouse_spaces w1JOIN warehouse_spaces w2ON w1.space_id < w2.space_idAND ((w1.occupied_start_time <= w2.occupied_start_time AND w1.occupied_end_time >= w2.occupied_start_time)OR (w1.occupied_start_time <= w2.occupied_end_time AND w1.occupied_end_time >= w2.occupied_end_time)OR (w2.occupied_start_time <= w1.occupied_start_time AND w2.occupied_end_time >= w1.occupied_start_time)OR (w2.occupied_start_time <= w1.occupied_end_time AND w2.occupied_end_time >= w1.occupied_end_time));
解释:
JOIN子句中的 w1.space_id < w2.space_id
是为了避免同一个货位的时间区间与自身比较,同时也避免重复比较(例如,比较了和后,就不需要再比较和)。AND后面的条件部分就是根据前面提到的重叠判定条件来判断两个时间区间是否重叠。 这个查询会返回存在占用时间重叠的货位对以及它们的占用时间区间。
- 思路
首先,通过窗口函数计算每个货位与其他货位在时间上的重叠情况。可以使用排序和范围窗口来比较相邻货位的时间区间,标记出重叠的货位。
SELECT space_id, occupied_start_time, occupied_end_time,MAX(CASEWHEN LAG(occupied_end_time) OVER (ORDER BY occupied_start_time) >= occupied_start_timeTHEN 1ELSE 0END) OVER (ORDER BY occupied_start_time) AS overlap_flagFROM warehouse_spaces;
- 解释
LAG(occupied_end_time) OVER (ORDER BY occupied_start_time)用于获取前一个货位的占用结束时间。 然后通过比较前一个货位的占用结束时间和当前货位的占用开始时间( LAG(occupied_end_time) OVER (ORDER BY occupied_start_time) >= occupied_start_time
),如果满足条件,则将overlap_flag
设为 1,表示有重叠;否则设为 0。最后,通过 MAX
函数在窗口内(按照占用开始时间排序的窗口)计算重叠标志,这样可以处理连续的重叠情况。
会飞的一十六
微信号:ddan_hashcode
扫描右侧二维码关注我们
点个【在看】 你最好看

文章转载自会飞的一十六,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。





