简体   繁体   English

基于先前数据的SQL透视

[英]SQL Pivoting based on previous data

This is my first question and truthfully i haven't did any search before posting this question because i don't know what this topic would categorized as. 这是我的第一个问题,说实话,在发布此问题之前,我没有进行任何搜索,因为我不知道该主题会被归类为什么。

This is a SQL related question where i need to, i guess Pivot, the data to get a more friendlier data output. 这是一个与SQL有关的问题,我想我需要在数据中获取更友好的数据输出。

I have a 4 columns table, respectively: ID, Username, Status, DateTime. 我有一个4列的表格,分别是:ID,用户名,状态,日期时间。 Status is what determine the user action, like login and logout. 状态是决定用户操作(如登录和注销)的因素。

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

I need to filter Status0 and Status1 as login and logout, so any Status0 in between would be ignored 我需要将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 

would resulted in 会导致

Username  Status0              Status1
A         2017-10-20 05:00:00  2017-10-20 09:50:55

and the next 'A' will search for Status0's DateTime that is bigger than or equal the last Status1 (2017-10-20 09:50:55) and so on until end of data 然后下一个'A'将搜索大于或等于最后一个Status1(2017-10-20 09:50:55)的Status0的DateTime,依此类推,直到数据结束

The end data i need would be formatted as below: 我需要的最终数据格式如下:

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  

How can i achieve this result? 我怎样才能达到这个结果? My logic says that i need to recursively compare the Status1 and find the next Status0, but i'm not sure how to put it in sql query. 我的逻辑说我需要递归比较Status1并找到下一个Status0,但是我不确定如何将其放入sql查询中。

Any help is appreciated. 任何帮助表示赞赏。 Thanks before. 之前谢谢。

EDIT: I'm using SQL Server 2008. 编辑:我正在使用SQL Server 2008。

Hmmm. Here is one way: look up the next status 1 (using apply ) and then aggregate: 这是一种方法:查找下一个状态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);

This will give you a row for every status 1 datetime. 这将为每个状态1 datetime提供一行。

Try the following approach. 请尝试以下方法。 You remove repeating 0 in user status using LAG and then you use ROW_NUMBER to group appropriately your user's login/logout. 使用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

demo 演示

EDIT: ok, so the solution should be for SQL Server 2008 R2, therefore, there is no LAG . 编辑:好的,所以解决方案应该是针对SQL Server 2008 R2,因此,没有LAG Then it is possible to solve it using NOT EXISTS , however, it is not very readable: 然后可以使用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

demo 演示

ok and the there is a last version that uses outer joins and GROUP BY instead of dependent subqueries. 好的,还有一个使用外部联接和GROUP BY而不是依赖子查询的最新版本。 Therefore, it can be slightly more efficient in certain cases 因此,在某些情况下它可能会稍微更有效

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM