有关于如何编写自联接查询的问题。 在线会话表包含所有用户活动。每个Activity都有一个State ID,TimeStap来记录用户登录时间。
就像: 例如:
State TimeStamp User
X 1300 A
Y 1700 A
X 0700 B
Z 1500 B
Y 1600 B
X 2100 C
一点说明:在上表中,用户A在1300上登录状态X,然后在1700登录状态Y,因此用户A在状态X中花费0400(假设它是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
编辑:只是让问题更清楚。现在让我们假设它在SQL Server DMBS中,但是可以使用其他DBMS。
输入时间戳存储为默认日期时间格式,如YYYY-MM-DD HH:MM:SS。
答案 0 :(得分:0)
你没有提到你正在使用哪个DBMS,所以我写这个我是如何在MS SQL Server(TSQL)中做的。您需要访问LAG
功能,这不是通用的。
LAG
的作用是允许您根据某些共享列值比较前一行的值,在本例中为User
。此代码捕获prev_
字段中的这些比较。我正在使用count()
来区分具有多行的用户与只有一行的用户。单行用户在union all
之后单独处理。
您会注意到,在最终输出步骤之前,我没有使用您的字段名称。这是因为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
答案 1 :(得分:0)
我有一个使用Oracle分析函数的解决方案,您可能无法使用它。我也使用时间戳作为oracle varchars。
我在子查询中使用LEAD()来返回"下一个用户"和#34;下一次"。 然后使用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;
答案 2 :(得分:0)
将您的输入包含在WITH子句中(我使用TIMESTAMP类型作为“timestamp”;如果您对列名使用保留字(“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