【问题标题】:Log Table aggregation日志表聚合
【发布时间】:2016-08-23 17:10:54
【问题描述】:

我正在寻找在日志中聚合数据的最佳方法。下表是“谁在车站”的日志。例如

User1 在“10/2/2014 14:46”和“10/2/2014 14:50”之间在 station1(2 个交易)

用户 2 在“2014 年 10 月 2 日 15:00”和“2014 年 10 月 2 日 15:15”之间在 station1(5 次交易)

user3 于 2014 年 10 月 3 日 16:31 在 station1 上(1 笔交易)

用户 2 在“2014 年 10 月 2 日 17:04”和“2014 年 10 月 2 日 17:06”之间再次在同一天在 station1 上(2 次交易)

    Station1 	10/2/2014 14:46	User1
Station1 	10/2/2014 14:50	User1
Station1 	10/2/2014 15:00	User2
Station1 	10/2/2014 15:00	User2
Station1 	10/2/2014 15:00	User2
Station1 	10/2/2014 15:00	User2
Station1 	10/2/2014 15:15	User2
Station1 	10/2/2014 16:31	User3
Station1 	10/2/2014 17:04	User2
Station1 	10/2/2014 17:06	User2

我正在寻找类似“用户在工作站上停留了多长时间以及交易次数”之类的输出......是否可以在不遍历每个项目的情况下做同样的事情?如果是这样,我该怎么做?

station     User    start time        Duration    Transactions
Station1 	User1	10/2/2014 14:46	  4 min       2
Station1 	User2	10/2/2014 15:00	  15 min      5
Station1 	User3	10/2/2014 16:31	              1
Station1 	User2	10/2/2014 15:04   2 min       2

【问题讨论】:

  • 我在 SQL Server 2012 上。
  • 为什么 Station1 10/2/2014 15:00 User2 有多行?
  • 我的要求是获取用户每次访问车站的详细信息...(在给定时间内只能有一个用户在车站)。
  • 在这种情况下,用户 2 于 2014 年 10 月 2 日访问了 2 次​​span>
  • 我明白了,但有 4 条记录具有相同的电台、用户和时间。无论如何,我会解决它

标签: sql-server tsql datetime sql-server-2012


【解决方案1】:

设置数据:

create table #log (Station varchar(20), Dt datetime, UserName varchar(10))

insert into #log values
('Station1',    '10/2/2014 14:46',  'User1'),
('Station1',    '10/2/2014 14:50',  'User1'),
('Station1',    '10/2/2014 15:00',  'User2'),
('Station1',    '10/2/2014 15:00',  'User2'),
('Station1',    '10/2/2014 15:00',  'User2'),
('Station1',    '10/2/2014 15:00',  'User2'),
('Station1',    '10/2/2014 15:15',  'User2'),
('Station1',    '10/2/2014 16:31',  'User3'),
('Station1',    '10/2/2014 17:04',  'User2'),
('Station1',    '10/2/2014 17:06',  'User2')

查询:

;with TranCount as (
select Station, UserName, count(*) Cnt, MIN(Dt) MinDt
from (select t.*,
             (row_number() over (partition by Station order by Dt) -
              row_number() over (partition by UserName order by Dt)
             ) as grp
      from #log t
     ) t
group by grp, Station, UserName
)
-- find time from next record. 
-- also number the records to find IN and OUT entries
, cte1 as ( 
      select Station, UserName, Dt
        , LEAD(Dt) over (partition by Station, UserName order by Station, Dt) NextDt
        , ROW_NUMBER() over (partition by Station, UserName order by Station, Dt) as RN
      from #log
      group by Station, UserName, Dt
)
-- only select IN entries, the exit time will be in NextDt field. 
-- so ignore the even rows.
, cte2 as ( 
      select *
      from cte1
      where RN % 2 = 1
)
select cte2.Station, cte2.UserName, Dt StartTime
    , DATEDIFF(SECOND, Dt, NextDt) / 60 AS Duration, TranCount.Cnt AS TransactionCount
from cte2
    inner join TranCount on TranCount.Station = cte2.Station 
        and TranCount.UserName = cte2.UserName 
        and TranCount.MinDt = cte2.Dt
order by Station, Dt, UserName

结果:

+----------+----------+-------------------------+----------+------------------+
| Station  | UserName |        StartTime        | Duration | TransactionCount |
+----------+----------+-------------------------+----------+------------------+
| Station1 | User1    | 2014-10-02 14:46:00.000 | 4        |                2 |
| Station1 | User2    | 2014-10-02 15:00:00.000 | 15       |                5 |
| Station1 | User3    | 2014-10-02 16:31:00.000 | NULL     |                1 |
| Station1 | User2    | 2014-10-02 17:04:00.000 | 2        |                2 |
+----------+----------+-------------------------+----------+------------------+

如果帖子回答了问题,请“标记为答案”

【讨论】:

    【解决方案2】:

    运行这些脚本

    脚本 1

    -- =============================================
    -- Author:      Carlos Aguilar
    -- Create date:  April 2016
    -- Description:  Massive Creation of Log Tables in a Database
    -- =============================================
    
    DECLARE @SCRIPT NVARCHAR(MAX)
    DECLARE @TABLE NVARCHAR(MAX)
    DECLARE @COLUMNS NVARCHAR(MAX)
    
    DECLARE _LOGS_ CURSOR FOR 
        SELECT NAME FROM SYSOBJECTS WHERE TYPE = 'U' AND NAME NOT LIKE '__LOG_%' 
    
    OPEN _LOGS_
    
    
    
    BEGIN TRY
    
      BEGIN TRAN
    
      FETCH NEXT FROM _LOGS_ INTO @TABLE
    
      WHILE (@@FETCH_STATUS = 0)
      BEGIN
    
        SET @COLUMNS = ''
    
        SELECT @COLUMNS = @COLUMNS + '[' + T1.NAME + '] ' + T2.NAME + 
            CASE 
                WHEN T1.XTYPE IN (106, 108) THEN
                    '(' + CONVERT(VARCHAR, T1.XPREC) + ', ' + CONVERT(VARCHAR, T1.XSCALE) + ')'     
                WHEN T1.XTYPE IN (173, 175, 42, 43, 239, 231, 41, 165, 167) THEN
                    '(' + CASE WHEN T1.LENGTH = -1 THEN 'MAX' ELSE CONVERT(VARCHAR, T1.LENGTH ) END  + ')'
                ELSE
                    ''
             END
             + ' NULL, '
        FROM SYSOBJECTS T0 
            INNER JOIN SYSCOLUMNS T1 
                ON T0.ID = T1.ID
            INNER JOIN sys.TYPES T2
                ON T1.XTYPE = T2.SYSTEM_TYPE_ID 
        WHERE T0.TYPE = 'U' AND 
            T0.NAME NOT LIKE '__LOG_%' AND
            T0.NAME = @TABLE AND
            T1.XTYPE NOT IN (34, 35, 99) AND
            T2.NAME <> 'sysname'
    
        SELECT @SCRIPT = 
        'CREATE TABLE __LOG_' + @TABLE + '(' + @COLUMNS + '
            [__DB_USER] nvarchar(128) NULL,
            [__APP_NAME] nvarchar(128) NULL,
            [__HOST_NAME] nvarchar(128) NULL,
            [__IP_ADD] sql_variant NULL,
            [__EVENT] varchar(1) NULL,
            [__TIME] datetime  NULL,
            [__PROTOCOL] sql_variant NULL) '
    
        PRINT @SCRIPT
    
        EXEC (@SCRIPT)
    
        SELECT @SCRIPT = 'DELETE FROM __LOG_' + @TABLE
    
        PRINT @SCRIPT
    
        EXEC (@SCRIPT)   
    
        FETCH NEXT FROM _LOGS_ INTO @TABLE
    
      END   
    
      COMMIT
    
    END TRY
    BEGIN CATCH
    
        ROLLBACK
        PRINT 'ERROR: ' + ERROR_MESSAGE()
    
    END CATCH
    
    CLOSE _LOGS_
    DEALLOCATE _LOGS_
    
    GO
    

    脚本 2

    -- =============================================
    -- Author:      Carlos Aguilar
    -- Create date:  April 2016
    -- Description:  Massive Creation of Trigger for Log Tables in a Database
    -- =============================================
    
    DECLARE @COLUMNS NVARCHAR(MAX)
    DECLARE @SCRIPT NVARCHAR(MAX)
    DECLARE @TABLE NVARCHAR(MAX)
    
    DECLARE _LOGS_ CURSOR FOR 
        SELECT NAME FROM SYSOBJECTS WHERE TYPE = 'U' AND NAME NOT LIKE '__LOG_%'
    
    OPEN _LOGS_
    
    
    
    BEGIN TRY
    
      BEGIN TRAN
    
      FETCH NEXT FROM _LOGS_ INTO @TABLE
    
      WHILE (@@FETCH_STATUS = 0)
      BEGIN
    
        SET @COLUMNS = ''
    
        SELECT @COLUMNS = @COLUMNS + '[' + T1.NAME + '], ' 
        FROM SYSOBJECTS T0 
            INNER JOIN SYSCOLUMNS T1 
                ON T0.ID = T1.ID 
        WHERE T0.TYPE = 'U' AND 
            T0.NAME NOT LIKE '__LOG_%' AND
            T0.NAME = @TABLE AND
            T1.XTYPE NOT IN (34, 35, 99)
    
        SELECT @SCRIPT = 'CREATE TRIGGER [dbo].[__LOG_INSERT_' + @TABLE + '] ON [dbo].[' + @TABLE + ']
                    WITH ENCRYPTION, EXECUTE AS CALLER
                    FOR INSERT
                    AS
                    BEGIN
                      INSERT INTO __LOG_' + @TABLE + '(' +
                        @COLUMNS + '
                        __DB_USER,
                        __APP_NAME,
                        __HOST_NAME,
                        __IP_ADD,
                        __EVENT,
                        __TIME,
                        __PROTOCOL) 
                      SELECT ' + @COLUMNS + '
                            SUSER_SNAME() AS __DB_USER , 
                            APP_NAME  () AS __APP_NAME,
                            HOST_NAME () AS __HOST_NAME,
                            CONNECTIONPROPERTY(''client_net_address'') __IP_ADD,
                            ''I'' AS __EVENT,
                            GETDATE() AS __TIME,
                            CONNECTIONPROPERTY(''protocol_type'') AS __PROTOCOL
                      FROM INSERTED
    
                    END'
    
          PRINT @SCRIPT
    
          EXEC (@SCRIPT)
    
          SELECT @SCRIPT = 'CREATE TRIGGER [dbo].[__LOG_UPDATE_' + @TABLE + '] ON [dbo].[' + @TABLE + ']
                    WITH ENCRYPTION, EXECUTE AS CALLER
                    FOR UPDATE
                    AS
                    BEGIN
                      INSERT INTO __LOG_' + @TABLE + '(' +
                        @COLUMNS + '
                        __DB_USER,
                        __APP_NAME,
                        __HOST_NAME,
                        __IP_ADD,
                        __EVENT,
                        __TIME,
                        __PROTOCOL) 
                      SELECT ' + @COLUMNS + '
                            SUSER_SNAME() AS __DB_USER , 
                            APP_NAME  () AS __APP_NAME,
                            HOST_NAME () AS __HOST_NAME,
                            CONNECTIONPROPERTY(''client_net_address'') __IP_ADD,
                            ''U'' AS __EVENT,
                            GETDATE() AS __TIME,
                            CONNECTIONPROPERTY(''protocol_type'') AS __PROTOCOL
                      FROM INSERTED
    
                    END'
    
          PRINT @SCRIPT
    
          EXEC (@SCRIPT)
    
          SELECT @SCRIPT = 'CREATE TRIGGER [dbo].[__LOG_DELETE_' + @TABLE + '] ON [dbo].[' + @TABLE + ']
                    WITH ENCRYPTION, EXECUTE AS CALLER
                    FOR DELETE
                    AS
                    BEGIN
                      INSERT INTO __LOG_' + @TABLE + '(' +
                        @COLUMNS + '
                        __DB_USER,
                        __APP_NAME,
                        __HOST_NAME,
                        __IP_ADD,
                        __EVENT,
                        __TIME,
                        __PROTOCOL) 
                      SELECT ' + @COLUMNS + '
                            SUSER_SNAME() AS __DB_USER , 
                            APP_NAME  () AS __APP_NAME,
                            HOST_NAME () AS __HOST_NAME,
                            CONNECTIONPROPERTY(''client_net_address'') __IP_ADD,
                            ''D'' AS __EVENT,
                            GETDATE() AS __TIME,
                            CONNECTIONPROPERTY(''protocol_type'') AS __PROTOCOL
                      FROM DELETED
    
                    END'
    
          PRINT @SCRIPT
    
          EXEC (@SCRIPT)
    
        FETCH NEXT FROM _LOGS_ INTO @TABLE
    
      END   
    
      COMMIT
    
    END TRY
    BEGIN CATCH
    
        ROLLBACK
        PRINT 'ERROR: ' + ERROR_MESSAGE()
    
    END CATCH
    
    CLOSE _LOGS_
    DEALLOCATE _LOGS_
    
    GO
    

    脚本 3

    -- =============================================
    -- Author:      Carlos Aguilar
    -- Create date:  April 2016
    -- =============================================
    
    CREATE PROCEDURE dbo.SelectLogByDate
    (
        @StartDate DATETIME,
        @EndDate DATETIME
    )
    AS
    BEGIN
    
        CREATE TABLE #TRANSACTIONS
        (
            [TIMESTAMP] DATETIME,
            STATION NVARCHAR(128),
            [USER] NVARCHAR(128)
        )
    
        DECLARE @TABLE NVARCHAR(MAX)
        DECLARE @SQL NVARCHAR(MAX)
        DECLARE @ParmDefinition NVARCHAR(MAX);
    
        SET @ParmDefinition = N'@StartDate DATETIME, @EndDate DATETIME'
    
        DECLARE _LOGS_ CURSOR FOR 
        SELECT NAME FROM SYSOBJECTS WHERE TYPE = 'U' AND NAME LIKE '__LOG_%' 
    
        OPEN _LOGS_
    
        BEGIN TRY
    
            BEGIN TRAN
    
                FETCH NEXT FROM _LOGS_ INTO @TABLE
    
                WHILE (@@FETCH_STATUS = 0)
                BEGIN
    
                    SELECT @SQL = 'INSERT INTO #TRANSACTIONS ([TIMESTAMP], STATION, [USER]) SELECT __TIME, __HOST_NAME, __DB_USER FROM ' + 
                        @TABLE + ' ' +
                        'WHERE __TIME BETWEEN @StartDate AND @EndDate'
    
                    EXECUTE sp_executesql @SQL, @ParmDefinition,
                          @StartDate = @StartDate,
                          @EndDate = @EndDate
    
                    FETCH NEXT FROM _LOGS_ INTO @TABLE
                END
            COMMIT
    
        END TRY
        BEGIN CATCH
    
        END CATCH
        CLOSE _LOGS_
        DEALLOCATE _LOGS_
    
        SELECT STATION, 
                [USER],
                MIN([TIMESTAMP]) AS "START TIME",
                MAX([TIMESTAMP]) AS "END TIME",
                DATEDIFF(MINUTE, MIN([TIMESTAMP]), MAX([TIMESTAMP])) AS DURATION,
                COUNT(*) AS TRANSACTIONS
         FROM #TRANSACTIONS
         GROUP BY STATION, 
                [USER]
    
    END
    GO
    

    等待 1 或 2 天让用户事务运行

    DECLARE @return_value int
    
    EXEC    @return_value = [dbo].[SelectLogByDate]
            @StartDate = ?, --Param here
            @EndDate = ? --Param here
    
    SELECT  'Return Value' = @return_value
    
    GO
    

    【讨论】:

    • 感谢您回答问题,但您不应引用个人共享文件夹。而是将所有脚本写在答案正文中。
    • 您应该编辑您的问题并在一篇文章中提供所有脚本。我认为您已在单独的帖子中发布了每个脚本。此外,每当您想更改答案时,请编辑您的问题,不要发布新答案,即使您提供的解决方案不止一种,也应该在一篇文章中。
    猜你喜欢
    • 2019-10-29
    • 2020-01-26
    • 2011-06-07
    • 2020-09-16
    • 2020-10-27
    • 2023-03-24
    • 1970-01-01
    • 2014-08-10
    • 2017-10-02
    相关资源
    最近更新 更多