这是我的第一个问题,如果我在发布此问题之前没有进行任何搜索,因为我不知道这个主题会被归类为什么。
这是一个SQL相关的问题,我想Pivot需要数据来获得更友好的数据输出。
我有一个4列表,分别是:ID,Username,Status,DateTime。 状态是决定用户操作的因素,例如登录和注销。
ID Username Status DateTime
1 A 0 2017-10-20 05:00:00
2 A 0 2017-10-20 07:23:10
3 B 0 2017-10-20 07:24:45
4 A 1 2017-10-20 09:50:55
5 A 0 2017-10-20 13:00:56
6 B 1 2017-10-20 17:13:28
7 B 0 2017-10-20 17:50:47
8 A 1 2017-10-20 21:38:17
9 A 0 2017-10-20 21:38:19
10 B 1 2017-10-20 21:40:02
我需要将Status0和Status1过滤为登录和注销,因此中间的任何Status0都将被忽略
ID Username Status DateTime
1 A 0 2017-10-20 05:00:00
2 A 0 2017-10-20 07:23:10
4 A 1 2017-10-20 09:50:55
会导致
Username Status0 Status1
A 2017-10-20 05:00:00 2017-10-20 09:50:55
和下一个' A'将搜索Status0的DateTime,该DateTime大于或等于上一个Status1(2017-10-20 09:50:55),依此类推,直到数据结束
我需要的最终数据格式如下:
Username Status0 Status1
A 2017-10-20 05:00:00 2017-10-20 09:50:55
B 2017-10-20 07:24:45 2017-10-20 17:13:28
A 2017-10-20 13:00:56 2017-10-20 21:38:17
B 2017-10-20 17:50:47 2017-10-20 21:40:02
A 2017-10-20 21:38:19 null
我如何实现这一结果?我的逻辑说我需要递归地比较Status1并找到下一个Status0,但是我不确定如何将它放在sql查询中。
感谢任何帮助。谢谢。
编辑:我正在使用SQL Server 2008。
答案 0 :(得分:0)
嗯。这是一种方法:查找下一个状态1(使用apply
),然后聚合:
select username,
min(datetime) as status_0_datetime,
status_1_datetime
from (select t.*, t2.datetime as status_1_datetime
from t outer apply
(select top 1 t2.*
from t t2
where t2.username = t.username and t2.status = 1 and
t2.datetime > t.datetime
order by t2.datetime desc
) t2
where t.status = 0
) t
group by username, status_1_datetime
order by username, min(datetime);
这将为每个状态1日期时间提供一行。
答案 1 :(得分:0)
尝试以下方法。您使用LAG
删除用户状态中的重复0,然后使用ROW_NUMBER
对用户的登录/注销进行适当分组。
select tt.username,
MAX(CASE WHEN status = 0 THEN tt.datetime END) status0,
MAX(CASE WHEN status = 1 THEN tt.datetime END) status1
from
(
select *,
row_number() over (partition by username, status order by datetime) rn
from
(
select *, lag(status) over (partition by username order by datetime) prevstatus
from your_table
) t
where not(t.status = 0 and t.prevstatus = 0) or t.prevstatus is null -- this is to remove repeating 0
) tt
group by tt.username, tt.rn
编辑:确定,因此解决方案应该适用于SQL Server 2008 R2,因此,没有LAG
。然后可以使用NOT EXISTS
来解决它,但是,它不是非常易读:
select tt.username,
MAX(CASE WHEN status = 0 THEN tt.datetime END) status0,
MAX(CASE WHEN status = 1 THEN tt.datetime END) status1
from
(
select *,
row_number() over (partition by username, status order by datetime) rn
from
(
select *
from your_table yt1
where status = 1 or
not exists(
select 1
from your_table yt2
where yt2.status = 0 and
yt2.username = yt1.username and
yt2.datetime = (
select max(yt3.datetime)
from your_table yt3
where yt3.datetime < yt1.datetime and
yt3.username = yt1.username
)
)
) t
) tt
group by tt.username, tt.rn
确定,最后一个版本使用外部联接和GROUP BY
而不是依赖子查询。因此,在某些情况下可以稍微提高效率
select tt.username,
MAX(CASE WHEN status = 0 THEN tt.datetime END) status0,
MAX(CASE WHEN status = 1 THEN tt.datetime END) status1
from
(
select *,
row_number() over (partition by username, status order by datetime) rn
from
(
select xt.*, yt.status joinstatus
from your_table yt
right join (
select yt1.id, yt1.datetime, yt1.username, yt1.status, max(yt2.datetime) prevdatetime
from your_table yt1
left join your_table yt2 on yt1.datetime > yt2.datetime and
yt2.username = yt1.username and
yt1.status = 0
group by yt1.id, yt1.datetime, yt1.username, yt1.status
) xt on yt.datetime = xt.prevdatetime and yt.username = xt.username and xt.status = yt.status
) t
where t.joinstatus is null
) tt
group by tt.username, tt.rn