Sql 查找用户联机时间

Sql 查找用户联机时间,sql,Sql,对如何编写自连接查询有疑问。 联机会话表包含所有用户活动。每个活动都有一个状态ID TimeStap来记录用户登录时间 就像: 例如: 一点说明:在上表中,用户A在1300时登录到状态X,然后在1700时登录到状态Y,因此用户A花费0400时间假设在状态X时为4小时。 同样的逻辑也适用于用户B。 然后是用户C,因为它永远不会改变状态,所以我们使用当前时间-登录时间戳X 输出应该如下所示: State Time User X 0400(or 4)

对如何编写自连接查询有疑问。 联机会话表包含所有用户活动。每个活动都有一个状态ID TimeStap来记录用户登录时间

就像: 例如:

一点说明:在上表中,用户A在1300时登录到状态X,然后在1700时登录到状态Y,因此用户A花费0400时间假设在状态X时为4小时。 同样的逻辑也适用于用户B。 然后是用户C,因为它永远不会改变状态,所以我们使用当前时间-登录时间戳X

输出应该如下所示:

State    Time             User
  X   0400(or 4)           A
  X   0800(or 8)           B
  Z   0100(or 1)           B
  X   result of Now-2100   C
....... 编辑:只是让问题更清楚。现在让我们假设它在SQLServerDMB中,但是使用其他DBMS也可以


输入的时间戳存储为默认的日期时间格式,如YYYY-MM-DD HH:MM:SS

您没有提到您使用的是哪种DBMS,所以我在MS SQL Server TSQL中写这篇文章。您需要访问LAG函数,它不是通用的

LAG的作用是允许您基于某个共享列值(在本例中为User)比较前一行的值。此代码在prev_uu字段中捕获这些比较。我使用count来区分多行用户和只有一行的用户。单行用户在联合后单独处理

您会注意到,直到最后一个输出步骤,我才使用您的字段名。这是因为State、Timestamp和User都是保留字,即在SQL代码中起作用的字。我强烈建议您使用非保留字的字段名

此代码确实有一个主要限制;如果不是同一天,它在现在减去时间部分不起作用。因此,在您的示例中,它必须在同一天的21:01和23:59之间工作。如果你想有力地做到这一点,你应该为你的时间使用datetime格式,这将使这更容易并消除限制。但这个答案适用于您的数据,因此:

SELECT 
    b.prev_state AS [State]
    ,b.Online_time - b.prev_time AS [Time]
    ,b.U_ID as [User]
FROM
    (SELECT 
        t.Online_state
        ,t.U_ID
        ,t.Online_time
        ,LAG(t.online_time) OVER (PARTITION BY t.U_ID ORDER BY t.U_ID, t.online_time) AS prev_time
        ,LAG(t.online_state) OVER (PARTITION BY t.U_ID ORDER BY t.U_ID, t.online_time) AS prev_state
    FROM online_t AS t
    inner join 
        (SELECT 
            U_ID, 
            count(U_ID) AS tot
        FROM online_t
        GROUP BY U_ID) AS a
        on t.U_ID = a.U_ID
    WHERE tot > 1) AS b
WHERE prev_time is not null

union all

SELECT
    t.Online_state AS [State]
    ,concat(datepart(hh,getdate()),'00') - t.Online_time AS [Time]
    ,t.U_ID AS [USER]
FROM online_t AS t 
inner join
    (SELECT 
        U_ID
        ,count(U_ID) as tot
    FROM online_t
    GROUP BY U_ID) as a
    on t.U_ID = a.U_ID
WHERE tot = 1

我有一个使用Oracle分析功能的解决方案,您可能无法使用。我还将时间戳用作oracle varchar

我在子查询中使用LEAD返回下一个用户和下一次。 然后使用CASE语句来处理不同的场景

SELECT M.THESTATE,
    CASE 
    WHEN M.USERID = M2.NEXT_USER THEN M2.NEXT_TIME-M.THETIME
    WHEN M.USERID <> M2.NEXT_USER THEN NULL
    ELSE M.THETIME-0 END AS TOTALTIME 
    ,M.USERID
    FROM MYTEST M
    JOIN 
    (
      SELECT USERID, THESTATE, THETIME
      ,LEAD(THETIME) OVER (ORDER BY USERID, THETIME) AS NEXT_TIME
      ,LEAD(USERID) OVER (ORDER BY USERID, THETIME) AS NEXT_USER
      FROM MYTEST
      ORDER BY USERID
    ) M2 ON M2.USERID = M.USERID AND M2.THESTATE=M.THESTATE
    WHERE 
      CASE     
      WHEN M.USERID = M2.NEXT_USER THEN M2.NEXT_TIME-M.THETIME
      WHEN M.USERID <> M2.NEXT_USER THEN NULL
      ELSE M.THETIME-0 END 
    IS NOT NULL;

将您的输入包含在WITH子句中,我使用时间戳类型作为您的时间戳;有些数据库不喜欢使用保留字user,timestamp作为列名,请尝试以下操作:

WITH
-- input, don't use in query
input(state,"timestamp","user") AS (
          SELECT 'X',TIMESTAMP '2017-03-15 13:00:00','A'
UNION ALL SELECT 'Y',TIMESTAMP '2017-03-15 17:00:00','A'
UNION ALL SELECT 'X',TIMESTAMP '2017-03-15 07:00:00','B'
UNION ALL SELECT 'Z',TIMESTAMP '2017-03-15 15:00:00','B'
UNION ALL SELECT 'Y',TIMESTAMP '2017-03-15 16:00:00','B'
UNION ALL SELECT 'X',TIMESTAMP '2017-03-15 21:00:00','C'
)
,
-- start real query here, comma above would 
-- be the WITH keyword
state_duration_user AS (
SELECT
  state
, IFNULL(
    LEAD("timestamp") OVER(ORDER BY "timestamp")
  , CURRENT_TIMESTAMP
  ) - "timestamp"
  AS "time"
, "user"
FROM input
)
SELECT 
  state
, CAST(SUM("time") AS TIME(0)) AS "time"
, "user"    
FROM state_duration_user
GROUP BY
  state
, "user"
;

state|time    |user
Y    |04:00:00|A
Y    |01:00:00|B
Z    |01:00:00|B
X    |02:00:00|A
X    |06:00:00|B
X    |07:59:19|C  

现在窗口函数非常通用。@Rominus将使用相同的方法/逻辑,如果我将时间戳存储为默认日期时间格式,如YYYY-MM-DD HH:MM:SS?总体逻辑不会改变,但“concat”行专门用于处理非日期时间格式。使用datetime,您可以跳过转换,只需从值中减去它自己,也不需要计算“tot”或任何超过“union”的部分。这是Microsoft Azure面试问题。结束
WITH
-- input, don't use in query
input(state,"timestamp","user") AS (
          SELECT 'X',TIMESTAMP '2017-03-15 13:00:00','A'
UNION ALL SELECT 'Y',TIMESTAMP '2017-03-15 17:00:00','A'
UNION ALL SELECT 'X',TIMESTAMP '2017-03-15 07:00:00','B'
UNION ALL SELECT 'Z',TIMESTAMP '2017-03-15 15:00:00','B'
UNION ALL SELECT 'Y',TIMESTAMP '2017-03-15 16:00:00','B'
UNION ALL SELECT 'X',TIMESTAMP '2017-03-15 21:00:00','C'
)
,
-- start real query here, comma above would 
-- be the WITH keyword
state_duration_user AS (
SELECT
  state
, IFNULL(
    LEAD("timestamp") OVER(ORDER BY "timestamp")
  , CURRENT_TIMESTAMP
  ) - "timestamp"
  AS "time"
, "user"
FROM input
)
SELECT 
  state
, CAST(SUM("time") AS TIME(0)) AS "time"
, "user"    
FROM state_duration_user
GROUP BY
  state
, "user"
;

state|time    |user
Y    |04:00:00|A
Y    |01:00:00|B
Z    |01:00:00|B
X    |02:00:00|A
X    |06:00:00|B
X    |07:59:19|C