SQL 为符号 in/sign 转出数据,按应用程序
SQL Pivoting data for sign in/sign out, by application
我有一个审计日志,用于存储在整个系统中一个或多个连接的应用程序上发生的事件。
DECLARE @startDate DATETIME;
DECLARE @endDate DATETIME;
DECLARE @staffID INT;
SET @staffID = 4;
SELECT
@startDate = dbo.Date(COALESCE(@startDate, DATEADD(day, -1, GETUTCDATE()))),
@endDate = dbo.EndOfDay(COALESCE(@endDate, GETUTCDATE()))
SELECT
l.RecordedOn, s.FirstName + ' ' + s.LastName + ' (#' + CAST(l.StaffIDAffected AS VARCHAR(MAX)) + ')' AS StaffName,
InOut = CASE WHEN (l.Type = 'AUS') THEN 'Sign In' WHEN (l.Type = 'AUSO') THEN 'Sign Out' END,
a.Name AS ApplicationName, l.ApplicationID
FROM Logs l
LEFT JOIN OtherDB.dbo.Staff s ON (s.ID = l.StaffIDAffected)
LEFT JOIN Applications a ON (l.ApplicationID = a.ID)
WHERE
l.Type in ('AUS','AUSO') AND
l.StaffIDAffected = @staffID AND
l.RecordedOn BETWEEN @startDate AND @endDate
ORDER BY s.FirstName + ' ' + s.LastName, l.RecordedOn ASC
本returns以下数据:
RecordedOn StaffName InOut ApplicationName
ApplicationID
2015-06-01 13:56:32.490 Joel Smith (#4) Sign In NULL 0
2015-06-01 14:05:02.900 Joel Smith (#4) Sign In NULL 0
2015-06-01 14:06:01.470 Joel Smith (#4) Sign Out NULL 0
2015-06-01 14:22:57.000 Joel Smith (#4) Sign In NULL 0
2015-06-01 14:23:04.170 Joel Smith (#4) Sign In NULL 0
2015-06-01 14:36:10.293 Joel Smith (#4) Sign In NULL 0
2015-06-01 14:47:38.993 Joel Smith (#4) Sign In NULL 0
2015-06-01 14:55:56.297 Joel Smith (#4) Sign In Admin Website 4
2015-06-01 14:56:33.107 Joel Smith (#4) Sign In Panel 2
2015-06-01 14:56:43.783 Joel Smith (#4) Sign Out NULL 0
2015-06-01 15:00:03.950 Joel Smith (#4) Sign In Panel 2
2015-06-01 15:06:33.403 Joel Smith (#4) Sign In Admin Website 4
2015-06-01 15:06:45.843 Joel Smith (#4) Sign Out Admin Website 4
2015-06-01 15:23:57.543 Joel Smith (#4) Sign In Panel 2
我想要的格式如下。我将空应用程序名称合并为 'General' 并将空应用程序 ID 合并为 -1。 (NULL 值会随着时间的推移而消失,但现在需要稍微优雅地处理它们)。
StaffName ApplicationName ApplicationID SignIn SignOut
Joel Smith (#4) General -1 2015-06-01 13:56:32.490 NULL
Joel Smith (#4) General -1 2015-06-01 14:05:02.900 2015-06-01 14:06:01.470
Joel Smith (#4) General -1 2015-06-01 14:22:57.000 NULL
Joel Smith (#4) General -1 2015-06-01 14:23:04.170 NULL
Joel Smith (#4) General -1 2015-06-01 14:36:10.293 NULL
Joel Smith (#4) General -1 2015-06-01 14:47:38.993 2015-06-01 14:56:43.783
Joel Smith (#4) Admin Website 4 2015-06-01 14:55:56.297 NULL
Joel Smith (#4) Panel 2 2015-06-01 14:56:33.107 NULL
Joel Smith (#4) Panel 2 2015-06-01 15:00:03.950 NULL
Joel Smith (#4) Admin Website 4 2015-06-01 15:06:33.403 2015-06-01 15:06:45.843
Joel Smith (#4) Panel 2 2015-06-01 15:23:57.543 NULL
请注意,它应该按应用程序 Name/Application ID 分隔 login/logout 次。
这是我目前拥有的,但缺少一些东西:
DECLARE @startDate DATETIME;
DECLARE @endDate DATETIME;
DECLARE @staffID INT;
SET @staffID = 4;
SELECT
@startDate = dbo.Date(COALESCE(@startDate, DATEADD(day, -1, GETUTCDATE()))),
@endDate = dbo.EndOfDay(COALESCE(@endDate, GETUTCDATE()))
SELECT
StaffName,
ApplicationName,
ApplicationID,
SignIn,
SignOut
FROM
(
SELECT
l.RecordedOn,
s.FirstName + ' ' + s.LastName + ' (#' + CAST(l.StaffIDAffected AS VARCHAR(MAX)) + ')' AS StaffName,
InOut = CASE WHEN (l.Type = 'AUS') THEN 'SignIn' WHEN (l.Type = 'AUSO') THEN 'SignOut' END,
COALESCE(a.Name, 'General') AS ApplicationName,
COALESCE(l.ApplicationID, -1) AS ApplicationID
FROM Logs l
LEFT JOIN OtherDB.dbo.Staff s ON (s.ID = l.StaffIDAffected)
LEFT JOIN Applications a ON (l.ApplicationID = a.ID)
WHERE
l.Type in ('AUS','AUSO') AND
l.StaffIDAffected = @staffID AND
l.RecordedOn BETWEEN @startDate AND @endDate
--ORDER BY s.FirstName + ' ' + s.LastName, l.RecordedOn ASC
) p
PIVOT
(
MIN(RecordedOn)
FOR InOut IN ([SignIn], [SignOut])
) pvt
此代码returns以下数据:
StaffName ApplicationName ApplicationID SignIn SignOut
Joel Smith (#4) Admin Website 4 2015-06-01 14:55:56.297 2015-06-01 15:06:45.843
Joel Smith (#4) General 0 2015-06-01 13:56:32.490 2015-06-01 14:06:01.470
Joel Smith (#4) Panel 2 2015-06-01 14:56:33.107 NULL
我做错了什么?我正在使用 SQL 服务器 2012/2014。
EDIT 我也试过没有一个有趣的结果的枢轴:
DECLARE @startDate DATETIME;
DECLARE @endDate DATETIME;
DECLARE @staffID INT;
SET @staffID = 4;
SELECT
@startDate = dbo.Date(COALESCE(@startDate, DATEADD(day, -1, GETUTCDATE()))),
@endDate = dbo.EndOfDay(COALESCE(@endDate, GETUTCDATE()))
;WITH cte1 AS
(
SELECT *
, ROW_NUMBER() OVER
(PARTITION BY StaffIDAffected, CAST(RecordedOn AS DATE) ORDER BY RecordedOn)
AS num
,CASE WHEN ([Type] = 'AUS') THEN 'Sign In' WHEN ([Type] = 'AUSO') THEN 'Sign Out' END AS [Status]
FROM Logs
WHERE [Type] IN ('AUS','AUSO')
AND StaffIDAffected = @staffID
AND RecordedOn BETWEEN @startDate AND @endDate
)
SELECT l1.StaffIDAffected
, l1.RecordedOn [SignIn]
, l2.RecordedOn [SignOut]
FROM cte1 l1
left outer JOIN
cte1 l2 ON
l2.StaffIDAffected = l1.StaffIDAffected
AND CAST(l2.RecordedOn AS DATE) = CAST(l1.RecordedOn AS DATE)
AND l2.num = l1.num + 1
WHERE l1.status = 'Sign In'
AND (l2.Status IS NULL OR l2.Status = 'Sign Out')
请注意,这个不包括应用程序,因为我只是想通过 NULL 应用程序来获取正确的值 name/ID...
4 2015-06-01 14:05:02.900 2015-06-01 14:06:01.470
4 2015-06-01 14:56:33.107 2015-06-01 14:56:43.783
4 2015-06-01 15:06:33.403 2015-06-01 15:06:45.843
4 2015-06-01 16:00:35.477 2015-06-01 16:01:47.703
4 2015-06-01 16:02:20.487 2015-06-01 16:03:34.827
4 2015-06-01 16:09:14.353 2015-06-01 16:09:22.213
4 2015-06-01 16:13:26.377 2015-06-01 16:14:01.560
因为我们没有fiddle我只能建议。 Pivot 将按子查询中不在 aggregation
部分和 spreading
部分中的列分组,即它将按 StaffName, ApplicationName, ApplicationID
分组。所以你最终会得到几行,其中有这些列的不同组合。
现在您想要所有 login
行最接近 logout
行。如果这是真的,那么你可以这样做:
select c1.ApplicationID,
c1.ApplicationName,
c1.StaffName,
c1.RecordedOn as SignIn,
oa.RecordedOn as SignOut
from cte1 c1
outer apply( select top 1 RecordedOn from cte2 c2
where c1.ApplicationID = c2.ApplicationID and
c1.StaffName = c2.StaffName and
c2.RecordedOn > c1.RecordedOn and
c2.InOut = 'Sign Out' and
c1.num + 1 = c2.num
order by c2.RecordedOn) oa
where c1.InOut = 'Sign In'
我有一个审计日志,用于存储在整个系统中一个或多个连接的应用程序上发生的事件。
DECLARE @startDate DATETIME;
DECLARE @endDate DATETIME;
DECLARE @staffID INT;
SET @staffID = 4;
SELECT
@startDate = dbo.Date(COALESCE(@startDate, DATEADD(day, -1, GETUTCDATE()))),
@endDate = dbo.EndOfDay(COALESCE(@endDate, GETUTCDATE()))
SELECT
l.RecordedOn, s.FirstName + ' ' + s.LastName + ' (#' + CAST(l.StaffIDAffected AS VARCHAR(MAX)) + ')' AS StaffName,
InOut = CASE WHEN (l.Type = 'AUS') THEN 'Sign In' WHEN (l.Type = 'AUSO') THEN 'Sign Out' END,
a.Name AS ApplicationName, l.ApplicationID
FROM Logs l
LEFT JOIN OtherDB.dbo.Staff s ON (s.ID = l.StaffIDAffected)
LEFT JOIN Applications a ON (l.ApplicationID = a.ID)
WHERE
l.Type in ('AUS','AUSO') AND
l.StaffIDAffected = @staffID AND
l.RecordedOn BETWEEN @startDate AND @endDate
ORDER BY s.FirstName + ' ' + s.LastName, l.RecordedOn ASC
本returns以下数据:
RecordedOn StaffName InOut ApplicationName
ApplicationID
2015-06-01 13:56:32.490 Joel Smith (#4) Sign In NULL 0
2015-06-01 14:05:02.900 Joel Smith (#4) Sign In NULL 0
2015-06-01 14:06:01.470 Joel Smith (#4) Sign Out NULL 0
2015-06-01 14:22:57.000 Joel Smith (#4) Sign In NULL 0
2015-06-01 14:23:04.170 Joel Smith (#4) Sign In NULL 0
2015-06-01 14:36:10.293 Joel Smith (#4) Sign In NULL 0
2015-06-01 14:47:38.993 Joel Smith (#4) Sign In NULL 0
2015-06-01 14:55:56.297 Joel Smith (#4) Sign In Admin Website 4
2015-06-01 14:56:33.107 Joel Smith (#4) Sign In Panel 2
2015-06-01 14:56:43.783 Joel Smith (#4) Sign Out NULL 0
2015-06-01 15:00:03.950 Joel Smith (#4) Sign In Panel 2
2015-06-01 15:06:33.403 Joel Smith (#4) Sign In Admin Website 4
2015-06-01 15:06:45.843 Joel Smith (#4) Sign Out Admin Website 4
2015-06-01 15:23:57.543 Joel Smith (#4) Sign In Panel 2
我想要的格式如下。我将空应用程序名称合并为 'General' 并将空应用程序 ID 合并为 -1。 (NULL 值会随着时间的推移而消失,但现在需要稍微优雅地处理它们)。
StaffName ApplicationName ApplicationID SignIn SignOut
Joel Smith (#4) General -1 2015-06-01 13:56:32.490 NULL
Joel Smith (#4) General -1 2015-06-01 14:05:02.900 2015-06-01 14:06:01.470
Joel Smith (#4) General -1 2015-06-01 14:22:57.000 NULL
Joel Smith (#4) General -1 2015-06-01 14:23:04.170 NULL
Joel Smith (#4) General -1 2015-06-01 14:36:10.293 NULL
Joel Smith (#4) General -1 2015-06-01 14:47:38.993 2015-06-01 14:56:43.783
Joel Smith (#4) Admin Website 4 2015-06-01 14:55:56.297 NULL
Joel Smith (#4) Panel 2 2015-06-01 14:56:33.107 NULL
Joel Smith (#4) Panel 2 2015-06-01 15:00:03.950 NULL
Joel Smith (#4) Admin Website 4 2015-06-01 15:06:33.403 2015-06-01 15:06:45.843
Joel Smith (#4) Panel 2 2015-06-01 15:23:57.543 NULL
请注意,它应该按应用程序 Name/Application ID 分隔 login/logout 次。
这是我目前拥有的,但缺少一些东西:
DECLARE @startDate DATETIME;
DECLARE @endDate DATETIME;
DECLARE @staffID INT;
SET @staffID = 4;
SELECT
@startDate = dbo.Date(COALESCE(@startDate, DATEADD(day, -1, GETUTCDATE()))),
@endDate = dbo.EndOfDay(COALESCE(@endDate, GETUTCDATE()))
SELECT
StaffName,
ApplicationName,
ApplicationID,
SignIn,
SignOut
FROM
(
SELECT
l.RecordedOn,
s.FirstName + ' ' + s.LastName + ' (#' + CAST(l.StaffIDAffected AS VARCHAR(MAX)) + ')' AS StaffName,
InOut = CASE WHEN (l.Type = 'AUS') THEN 'SignIn' WHEN (l.Type = 'AUSO') THEN 'SignOut' END,
COALESCE(a.Name, 'General') AS ApplicationName,
COALESCE(l.ApplicationID, -1) AS ApplicationID
FROM Logs l
LEFT JOIN OtherDB.dbo.Staff s ON (s.ID = l.StaffIDAffected)
LEFT JOIN Applications a ON (l.ApplicationID = a.ID)
WHERE
l.Type in ('AUS','AUSO') AND
l.StaffIDAffected = @staffID AND
l.RecordedOn BETWEEN @startDate AND @endDate
--ORDER BY s.FirstName + ' ' + s.LastName, l.RecordedOn ASC
) p
PIVOT
(
MIN(RecordedOn)
FOR InOut IN ([SignIn], [SignOut])
) pvt
此代码returns以下数据:
StaffName ApplicationName ApplicationID SignIn SignOut
Joel Smith (#4) Admin Website 4 2015-06-01 14:55:56.297 2015-06-01 15:06:45.843
Joel Smith (#4) General 0 2015-06-01 13:56:32.490 2015-06-01 14:06:01.470
Joel Smith (#4) Panel 2 2015-06-01 14:56:33.107 NULL
我做错了什么?我正在使用 SQL 服务器 2012/2014。
EDIT 我也试过没有一个有趣的结果的枢轴:
DECLARE @startDate DATETIME;
DECLARE @endDate DATETIME;
DECLARE @staffID INT;
SET @staffID = 4;
SELECT
@startDate = dbo.Date(COALESCE(@startDate, DATEADD(day, -1, GETUTCDATE()))),
@endDate = dbo.EndOfDay(COALESCE(@endDate, GETUTCDATE()))
;WITH cte1 AS
(
SELECT *
, ROW_NUMBER() OVER
(PARTITION BY StaffIDAffected, CAST(RecordedOn AS DATE) ORDER BY RecordedOn)
AS num
,CASE WHEN ([Type] = 'AUS') THEN 'Sign In' WHEN ([Type] = 'AUSO') THEN 'Sign Out' END AS [Status]
FROM Logs
WHERE [Type] IN ('AUS','AUSO')
AND StaffIDAffected = @staffID
AND RecordedOn BETWEEN @startDate AND @endDate
)
SELECT l1.StaffIDAffected
, l1.RecordedOn [SignIn]
, l2.RecordedOn [SignOut]
FROM cte1 l1
left outer JOIN
cte1 l2 ON
l2.StaffIDAffected = l1.StaffIDAffected
AND CAST(l2.RecordedOn AS DATE) = CAST(l1.RecordedOn AS DATE)
AND l2.num = l1.num + 1
WHERE l1.status = 'Sign In'
AND (l2.Status IS NULL OR l2.Status = 'Sign Out')
请注意,这个不包括应用程序,因为我只是想通过 NULL 应用程序来获取正确的值 name/ID...
4 2015-06-01 14:05:02.900 2015-06-01 14:06:01.470
4 2015-06-01 14:56:33.107 2015-06-01 14:56:43.783
4 2015-06-01 15:06:33.403 2015-06-01 15:06:45.843
4 2015-06-01 16:00:35.477 2015-06-01 16:01:47.703
4 2015-06-01 16:02:20.487 2015-06-01 16:03:34.827
4 2015-06-01 16:09:14.353 2015-06-01 16:09:22.213
4 2015-06-01 16:13:26.377 2015-06-01 16:14:01.560
因为我们没有fiddle我只能建议。 Pivot 将按子查询中不在 aggregation
部分和 spreading
部分中的列分组,即它将按 StaffName, ApplicationName, ApplicationID
分组。所以你最终会得到几行,其中有这些列的不同组合。
现在您想要所有 login
行最接近 logout
行。如果这是真的,那么你可以这样做:
select c1.ApplicationID,
c1.ApplicationName,
c1.StaffName,
c1.RecordedOn as SignIn,
oa.RecordedOn as SignOut
from cte1 c1
outer apply( select top 1 RecordedOn from cte2 c2
where c1.ApplicationID = c2.ApplicationID and
c1.StaffName = c2.StaffName and
c2.RecordedOn > c1.RecordedOn and
c2.InOut = 'Sign Out' and
c1.num + 1 = c2.num
order by c2.RecordedOn) oa
where c1.InOut = 'Sign In'