SQL给定类型的最后一个活动

时间:2015-03-20 15:09:00

标签: sql sql-server join

所以我有一个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,但我认为它更像是一个原则上的问题。

6 个答案:

答案 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记录以及稍后的时间(在同一日期)

Sample SQL Fiddle

答案 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 

原则:

  1. 首先,您要选择所有访客。
  2. 然后您正在申请访客上次活动
  3. 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
    
    1. 然后,您要从上一步中选择以获取空集,这将过滤掉最后一次活动不在&#39;中的访问者。如果上次活动是在&#39;你得到一行结果,然后应用工程。如果最后一项活动是“退出”。然后外部查询将导致空集,并且设计CROSS APPLY将消除此类访问者。