ROW_NUMBER 未按行顺序应用

ROW_NUMBER applied not in sequence of rows

使用 SQL 服务器 2012 我有一个要求,我必须稍微清理源以获得所需的输出。

我有车辆名称、GPSTime、Login/Logoff、DriverID 作为列。 由于源数据不干净,我们正尝试从中生成一个 table,每次登录都有后续注销时更干净。

我有如下数据

Vehicle         GPS      Login/Logoff  Driver 

Veh1 28-01-2016 06:30 Login D1 Veh1 28-01-2016 06:35 Login D1 Veh1 28-01-2016 06:40 Login D1 Veh1 28-01-2016 09:40 Logoff D1 Veh1 28-01-2016 10:30 Login D2 Veh1 28-01-2016 12:30 Logoff D2 Veh1 28-01-2016 15:30 Login D1 Veh1 28-01-2016 17:30 Logoff D1

我正在尝试将输出设为

Veh1 28-01-2016 06:30 Login D1 --> Tricky bit to take the first login Veh1 28-01-2016 09:40 Logoff D1 Veh1 28-01-2016 10:30 Login D2 Veh1 28-01-2016 12:30 Logoff D2 Veh1 28-01-2016 15:30 Login D1 Veh1 28-01-2016 17:30 Logoff D1

我尝试了 row_number、ROW_NUMBER() OVER (PARTITION BY DriverID ORDER BY GPSTime asc) 但无论我做什么,它都会为该驱动程序的所有登录连续添加一个序列号它不是相邻的行。我正计划使用所需的序列号来增强逻辑以得出所需的输出。

还有别的办法吗?任何建议都会有所帮助。

此致, 乔

所以分解问题。您拥有的是所有登录后注销的列表。

按您想要的顺序获取登录名列表很容易

 SELECT t1.VEH, t1.DRIVE, t1.TimeStamp
 FROM tablename t1
 WHERE t1.LoginLogoff = 'Login'
 ORDER BY t1.TimeStamp ASC

现在您必须 "insert" 注销到此列表中。如果你只是想要注销时间

简单的方法是做一个子查询并拉取你想要的值

 SELECT t1.VEH, t1.DRIVE, t1.TimeStamp as LoginTS,
    (Select t2.TimeStamp
     FROM tableName t2
     WHERE t2.LoginLogoff = 'Logoff'
       and t1.VEH = t2.VEH
       and t1.DRIVE = t2.DRIVE
       and t2.TimeStamp > t1.TimeStamp
     ORDER BY t2.TimeStamp ASC
     FETCH FIRST 1 ROW ONLY
   ) AS LogoutTS
 FROM tablename t1
 WHERE t1.LoginLogoff = 'Login'
 ORDER BY t1.TimeStamp ASC

您也可以将其作为联接来执行,但我会把它留给您。

如果你真的需要两行,你可以交叉连接到两行 table 到 "pull" 出你需要的值。

您的要求不是很清楚,但听起来您想删除连续登录或注销的行(每个驱动程序?),并且仅在 login/logoff 状态更改时保留第一行。

如果是这样,那么使用 lag() window 函数就可以派上用场了。

这是一个可以让您了解如何完成此任务的查询:

select t.vehicle, t.gps, t.login_logoff, t.driver
  from (select t.*,
               case 
                 when lag(login_logoff) 
                     over (partition by driver 
                               order by gps) = login_logoff
                 then 1 else 0 end as is_duplicate_row
          from tbl t) t
 where t.is_duplicate_row = 0
 order by t.gps

你可以在插入或更新这个table时增加一个额外的列并将其标记为suitable。这样做比编写复杂的查询更容易。

无论如何,我已经尝试过您现有的 table 设计 only.Also 我认为您需要每辆车的登录-注销状态。 您可以尝试使用其他示例数据,让我知道它在哪里不起作用。

declare @t table(Vehicle varchar(50),GPS datetime,Login_Logoff varchar(50),Driver varchar(50))
insert into @t values
 ('Veh1','2016-01-28 06:30','Login','D1')
,('Veh1','2016-01-28 06:35','Login','D1')
,('Veh1','2016-01-28 06:40','Login','D1')
,('Veh1','2016-01-28 09:40','Logoff','D1')
,('Veh1','2016-01-28 10:30','Login','D2')
,('Veh1','2016-01-28 12:30','Logoff','D2')
,('Veh1','2016-01-28 15:30','Login','D1')
,('Veh1','2016-01-28 17:30','Logoff','D1')

;

WITH CTE
AS (
    SELECT *
        ,row_number() OVER (
            PARTITION BY vehicle ORDER BY gps
            ) rn
    FROM @t
    )
    ,CTE1
AS (
    SELECT vehicle
        ,gps
        ,Login_Logoff
        ,driver
        ,rn
    FROM cte
    WHERE rn = 1

    UNION ALL

    SELECT a.vehicle
        ,CASE 
            WHEN b.driver = a.Driver
                AND a.Login_Logoff <> b.Login_Logoff
                THEN a.gps
            WHEN b.driver <> a.Driver
                THEN a.gps
            END
        ,a.login_logoff
        ,a.Driver
        ,b.rn + 1
    FROM cte a
    INNER JOIN CTE1 b ON a.rn = b.rn + 1
    WHERE a.rn <= 8
    )
SELECT vehicle
    ,gps
    ,Login_Logoff
    ,driver
FROM cte1
WHERE gps IS NOT NULL