用于登录/注销的 SQL 透视数据,按应用程序

标签 sql sql-server pivot

我有一个审计日志,用于存储在整个系统中一个或多个连接的应用程序上发生的事件。

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

这将返回以下数据: RecordedOn StaffName InOut ApplicationName 申请编号 2015-06-01 13:56:32.490 Joel Smith (#4) 登录 NULL 0 2015-06-01 14:05:02.900 Joel Smith (#4) 登录 NULL 0 2015-06-01 14:06:01.470 Joel Smith (#4) 注销 NULL 0 2015-06-01 14:22:57.000 Joel Smith (#4) 登录 NULL 0 2015-06-01 14:23:04.170 Joel Smith (#4) 登录 NULL 0 2015-06-01 14:36:10.293 Joel Smith (#4) 登录 NULL 0 2015-06-01 14:47:38.993 Joel Smith (#4) 登录 NULL 0 2015-06-01 14:55:56.297 Joel Smith (#4) 登录管理网站 4 2015-06-01 14:56:33.107 Joel Smith (#4) 登录面板 2 2015-06-01 14:56:43.783 Joel Smith (#4) 注销 NULL 0 2015-06-01 15:00:03.950 Joel Smith (#4) 登录面板 2 2015-06-01 15:06:33.403 Joel Smith (#4) 登录管理网站 4 2015-06-01 15:06:45.843 Joel Smith (#4) 登出管理网站 4 2015-06-01 15:23:57.543 Joel Smith (#4) 登录面板 2

我想要的格式如下。我正在将空应用程序名称合并为“常规”,并将空应用程序 ID 合并为 -1。 (NULL 值会随着时间的推移而消失,但现在需要稍微优雅地处理它们)。

StaffName ApplicationName ApplicationID 登录 退出 乔尔史密斯 (#4) 一般 -1 2015-06-01 13:56:32.490 NULL 乔尔·史密斯(#4)一般 -1 2015-06-01 14:05:02.900 2015-06-01 14:06:01.470 乔尔·史密斯 (#4) General -1 2015-06-01 14:22:57.000 NULL 乔尔·史密斯 (#4) General -1 2015-06-01 14:23:04.170 NULL 乔尔史密斯 (#4) General -1 2015-06-01 14:36:10.293 NULL 乔尔·史密斯 (#4) General -1 2015-06-01 14:47:38.993 2015-06-01 14:56:43.783 Joel Smith (#4) 管理网站 4 2015-06-01 14:55:56.297 NULL Joel Smith (#4) 面板 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) 管理网站 4 2015-06-01 15:06:33.403 2015-06-01 15:06:45.843 Joel Smith (#4) 面板 2 2015-06-01 15:23:57.543 NULL

请注意,应按应用程序名称/应用程序 ID 分隔登录/注销时间。

这是我目前所拥有的,但缺少一些东西:

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

此代码返回以下数据:

StaffName ApplicationName ApplicationID 登录 退出 Joel Smith (#4) 管理网站 4 2015-06-01 14:55:56.297 2015-06-01 15:06:45.843 乔尔·史密斯 (#4) 一般 0 2015-06-01 13:56:32.490 2015-06-01 14:06:01.470 Joel Smith (#4) 面板 2 2015-06-01 14:56:33.107 NULL

我做错了什么?我正在使用 SQL Server 2012/2014。

编辑 我也尝试过不使用枢轴并获得有趣的结果:

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 应用程序名称/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'

关于用于登录/注销的 SQL 透视数据,按应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30578114/

相关文章:

sql - 唯一约束列可以具有2个或多个空值吗? (甲骨文)

sql - 此问题的SQL查询将是什么?

c# - Sql Server临时表消失

sql-server - 基于 SQL AAD token 的身份验证 - 用户“NT AUTHORITY\ANONYMOUS LOGON”登录失败

sql - 减少天数 SQL

windows-phone-8 - 枢轴滑动方向检测

sql - Oracle:两个条件都为真时的条件非空约束

sql - 使用通用的相关子查询有效地提取不同的列

mysql - 如何在 MySQL 中返回数据透视表输出?

sql-server - 将垂直结果转换为水平模式(T-SQL)