所以我有一个Visitor表和一个Visitor_activity表。说:
访问者
Visitor_ID Int
Visitor_name varchar(20)
Visitor_Activity
ID Int
Visitor_ID Int
Activity_Type char(3) -- values IN or OUT
Activity_Time datetime
访客可能会在一天内多次登录和退出。
我想要一个很好的查询来告诉我所有访问者:即今天的最后一项活动(在activity_time上)是" IN"不是" OUT"。任何建议都非常感谢。 顺便说一句,它是T-SQL,但我认为它更像是一个原则上的问题。
答案 0 :(得分:2)
解决此问题的一种方法是使用相关的不存在谓词:
select Activity_Time, Visitor_ID
from Visitor_Activity t1
where Activity_Type = 'IN'
and not exists (
select 1
from Visitor_Activity
where Activity_Type = 'OUT'
and Visitor_ID = t1.Visitor_ID
and Activity_Time > t1.Activity_Time
and cast(Activity_Time as date) = cast(t1.Activity_Time as date)
)
这基本上说获取所有具有type = IN的visitor_id,其中不存在任何type = OUT记录以及稍后的时间(在同一日期)。
答案 1 :(得分:1)
SELECT
v.*
FROM
Visitors v
JOIN Visitor_Activity va ON va.Visitor_ID = v.Visitor_ID
WHERE
va.Activity_Type = 'IN'
AND NOT EXISTS ( SELECT
*
FROM
Visitor_Activity va_out
WHERE
va_out.Visitor_ID = va.Visitor_ID
AND va_out.Activity_Type = 'OUT'
AND va_out.Activity_Time > va.Activity_Time )
答案 2 :(得分:1)
with visitorsInOut as (
select Visitor_id,
max(case when Activity_Type = 'in' then Activity_Time else null end) inTime,
max(case when Activity_Type = 'out' then Activity_Time else null end) outTime
from Visitor_Activity
where datediff(dd, Activity_Time, getdate()) = 0
group by Visitor_id)
select Visitor_id
from visitorsInOut
where inTime > outTime or outTime is null
答案 3 :(得分:0)
这使用CTE查找具有最大Activity_Time的活动记录,其中Activity_Type ='IN'并将其分配给RowNum 1.然后您可以将CTE INNER JOIN连接到Visitor表,按CTE结果过滤RowNum = 1
; WITH VisAct AS(
SELECT act.Visitor_ID
, ROW_NUMBER() OVER(PARTITION BY Visitor_ID ORDER BY Activity_Time DESC) AS RowNum
FROM Visitor_Activity act
WHERE act.Activity_Type = 'IN'
AND act.Activity_Time >= CAST(GETDATE() AS DATE)
)
SELECT vis.Visitor_ID, vis.Visitor_name
FROM Visitor vis
INNER JOIN VisAct act
ON act.Visitor_ID = vis.Visitor_ID
WHERE act.Row_Num = 1
答案 4 :(得分:0)
您可以为每个访问者提取最新操作,然后只返回今天最后一个操作要签入的操作。
SELECT v.Visitor_ID, v.Visitor_Name, va.Activity_Type, va.Activity_Time
FROM Visitor AS v
INNER JOIN (SELECT Visitor_ID, Activity_Type, Activity_Time, RANK() OVER (PARTITION BY Visitor_ID ORDER BY Activity_Time DESC) AS LastAction
FROM Visitor_Activity
-- checks for today, can be omitted if you still want
-- to see someone checked in from yesterday
WHERE DATEDIFF(d, 0, Activity_Time) = DATEDIFF(d, 0, getdate())
) AS va ON va.Visitor_ID = v.Visitor_ID
WHERE LastAction = 1
AND Activity_Type = 'IN'
答案 5 :(得分:0)
使用CROSS APPLY
:
DECLARE @d DATE = '20150320'
DECLARE @v TABLE
(
visitor_id INT ,
visitor_name NVARCHAR(MAX)
)
DECLARE @a TABLE
(
visitor_id INT ,
type CHAR(3) ,
time DATETIME
)
INSERT INTO @v
VALUES ( 1, 'A' ),
( 2, 'B' ),
( 3, 'C' )
INSERT INTO @a
VALUES ( 1, 'in', '2015-03-20 19:32:27.513' ),
( 1, 'out', '2015-03-20 19:32:27.514' ),
( 1, 'in', '2015-03-20 19:32:27.515' ),
( 2, 'in', '2015-03-20 19:32:27.516' ),
( 2, 'out', '2015-03-20 19:32:27.517' ),
( 3, 'in', '2015-03-20 19:32:27.518' ),
( 3, 'out', '2015-03-20 19:32:27.519' ),
( 3, 'in', '2015-03-20 19:32:27.523' )
SELECT *
FROM @v v
CROSS APPLY ( SELECT *
FROM ( SELECT TOP 1
type
FROM @a a
WHERE a.visitor_id = v.visitor_id
AND a.time >= @d
AND a.time < DATEADD(dd, 1, @d)
ORDER BY time DESC
) i
WHERE type = 'in'
) c
输出:
visitor_id visitor_name type
1 A in
3 C in
原则:
SELECT TOP 1 type FROM @a a WHERE a.visitor_id = v.visitor_id AND a.time >= @d AND a.time < DATEADD(dd, 1, @d) ORDER BY time DESC
CROSS APPLY
将消除此类访问者。