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
使用 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