概要
本文通过一个计算汽车每日里程数的例子,展现如何通过汽车每日的总里程数,来计算汽车每日的里程数。
代码及实现
每辆汽车中都有一个里程数表,记录汽车从出场到当前行驶的里程数,下表是一样汽车的里程数表,该表已经进行简化,删去不相关的业务数据。
汽车终端每日会定时将当前的总里程数写入下表,下表的cars列作为汽车的唯一性标识; days标识具体哪一天; cumulative_distance 表示该车到当前这天的总里程数。
我们需要计算每辆汽车每天的里程数
表名是travel_car
基本思路
对于car1,在第一天其里程数是50,第二天应该是100-50,第三天应该是200-100。所以计算每天的里程数应该是当天的总里程数减去前一天的从里程数。相当于对每辆车的里程数做一次错位相减。
从数据库的查询的角度,我们需要获取每量车前一天的总里程数,也就是按照days排序的前一天数据的cumulative_distance 。
根据已有的需求,显然窗口函数是最佳解决方案。
代码实现
解决方案1
我们采用lag方法获取每辆车,前一天的里程数。由于涉及到较多子查询,所以选用CTE代替子查询。
with cte1 as (
select *,
isnull(lag(cumulative_distance) over (partition by cars order by [days]),0) as previousDayDistance
from travel_car
)
增加previousDayDistance列,通过lag函数找到每辆车前一天的里程数。对于day1而言,它没有前一条数据,所以会返回null, 因此使用isnull 方法,将null转成0。
然后将里程数相减,得到每日的里程数。
with cte1 as (
select *,
isnull(lag(cumulative_distance) over (partition by cars order by [days]),0) as previousDayDistance
from travel_car
)
select *, (cumulative_distance - previousDayDistance) as cuurent_day_distance from cte1
解决方案2
解决方案1中的lag方法只能在SQL Server 2014或更高的版本中运行。无法在低版本的SQL Server 中运行。
因此我们提出解决方案2。我们不再通过lag方法找到前一天每辆车的里程数,而是通过row_number方法进行排序和自连接来找到每辆车前一天的里程数。
with cte1 as (
select
*, ROW_NUMBER() over (partition by cars order by days) as rn
from travel_car)
,cte2 as (
select
t1.id, t1.cars, t1.[days], t1.cumulative_distance,isnull(t2.cumulative_distance, 0) as previousDayDistance ,t1.rn from cte1 t1
left join cte1 t2
on t1.rn = t2.rn+1 and t1.cars = t2.cars
)
select *, (cumulative_distance - previousDayDistance) as cuurent_day_distance from cte2
- 为每辆车生成row number,按照汽车分组,按照days排序;
- 按照rownumber+1和汽车的标识进行表格自连接,找到上一次的汽车里程数;
- 然后将里程数相减,得到每日的里程数。
附录
建表语句和数据初始化:
if OBJECT_ID('travel_car', 'U') is not null
drop table travel_car
create table travel_car (
id int primary key identity (1,1),
cars varchar(20) not null,
[days] varchar(40) not null,
cumulative_distance int not null
)
insert into travel_car (cars, [days], cumulative_distance) values
('car1','day1', 50),
('car1','day2', 100),
('car1','day3', 200),
('car2','day1', 0),
('car3','day1', 0),
('car3','day2', 50),
('car3','day3', 50),
('car3','day4', 100)