SQL 服务器 - CTE/Subqueries 性能优化 window 函数
SQL Server - CTE/Subqueries performance optimization with window functions
我正在使用 CTE 获得我想要的结果,但是有没有办法使用 window 函数来获得相同的结果,从而提高性能?
期望的输出:
id date Status
141 2015-03-01 00:00:00.000 free --> paid
141 2016-06-01 00:00:00.000 free --> paid
158 2015-08-01 00:00:00.000 free --> paid
CTE 代码:
declare @table01 table (id varchar(3), startdate datetime, enddate datetime, status varchar(10))
insert into @table01 (id, startdate, enddate, status)
values
('141','2015-01-01','2015-03-01','free'),
('141','2015-03-01','2015-07-01','paid'),
('141','2015-07-01','2015-11-01','closed'),
('141','2015-11-01','2016-02-01','paid'),
('141','2016-02-01','2016-06-01','free'),
('141','2016-06-01','2016-10-01','paid'),
('141','2016-10-01','2016-12-01','free'),
('141','2016-12-01','2017-04-01','closed'),
('158','2015-03-01','2015-08-01','free'),
('158','2015-08-01','2015-11-01','paid');
------------------------------------------------------------------------------
with sub01 as (
select id, enddate, status
from @table01
where status = 'free'
),
sub02 as (
select id, startdate, status
from @table01
where status = 'paid'
)
select a.id, b.startdate as [date], (a.status + ' --> ' + b.status) as [Status]
from sub01 a
left join sub02 b on a.id = b.id and a.enddate = b.startdate
where a.enddate = b.startdate
因为您只对状态 = free
和 paid
感兴趣,并且转换是在同一天进行的。它使用 case
语句 (case when status = 'free' then enddate else startdate end
) 来查找共同日期。最后按 id
、date
和条件 count(*) = 2
分组
这应该会给您带来更好的性能。
; with cte as
(
select id,
date = case when status = 'free' then enddate else startdate end,
status
from @table01
where status in ( 'free', 'paid' )
)
select id, date, status = 'free --> paid'
from cte
group by id, date
having count(*) = 2
order by id
如果我没理解错的话,你可以使用window函数:
select id, prev_date, 'free --> paid'
from (select t1.*,
lag(enddate) over (partition by id order by startdate) as prev_date,
lag(status) over (partition by id order by startdate) as prev_status
from table1 t1
) t1
where status = 'paid' and prev_status = 'free';
Here 是一个 db<>fiddle.
我正在使用 CTE 获得我想要的结果,但是有没有办法使用 window 函数来获得相同的结果,从而提高性能?
期望的输出:
id date Status
141 2015-03-01 00:00:00.000 free --> paid
141 2016-06-01 00:00:00.000 free --> paid
158 2015-08-01 00:00:00.000 free --> paid
CTE 代码:
declare @table01 table (id varchar(3), startdate datetime, enddate datetime, status varchar(10))
insert into @table01 (id, startdate, enddate, status)
values
('141','2015-01-01','2015-03-01','free'),
('141','2015-03-01','2015-07-01','paid'),
('141','2015-07-01','2015-11-01','closed'),
('141','2015-11-01','2016-02-01','paid'),
('141','2016-02-01','2016-06-01','free'),
('141','2016-06-01','2016-10-01','paid'),
('141','2016-10-01','2016-12-01','free'),
('141','2016-12-01','2017-04-01','closed'),
('158','2015-03-01','2015-08-01','free'),
('158','2015-08-01','2015-11-01','paid');
------------------------------------------------------------------------------
with sub01 as (
select id, enddate, status
from @table01
where status = 'free'
),
sub02 as (
select id, startdate, status
from @table01
where status = 'paid'
)
select a.id, b.startdate as [date], (a.status + ' --> ' + b.status) as [Status]
from sub01 a
left join sub02 b on a.id = b.id and a.enddate = b.startdate
where a.enddate = b.startdate
因为您只对状态 = free
和 paid
感兴趣,并且转换是在同一天进行的。它使用 case
语句 (case when status = 'free' then enddate else startdate end
) 来查找共同日期。最后按 id
、date
和条件 count(*) = 2
这应该会给您带来更好的性能。
; with cte as
(
select id,
date = case when status = 'free' then enddate else startdate end,
status
from @table01
where status in ( 'free', 'paid' )
)
select id, date, status = 'free --> paid'
from cte
group by id, date
having count(*) = 2
order by id
如果我没理解错的话,你可以使用window函数:
select id, prev_date, 'free --> paid'
from (select t1.*,
lag(enddate) over (partition by id order by startdate) as prev_date,
lag(status) over (partition by id order by startdate) as prev_status
from table1 t1
) t1
where status = 'paid' and prev_status = 'free';
Here 是一个 db<>fiddle.