日志表聚合

时间:2016-04-28 18:48:12

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

我正在寻找在日志中聚合数据的最佳方法。在下表中是"谁在一个电台"的日志。 E.g

用户1在#1; 2014年10月2日14:46"之间的电台1上和" 10/2/2014 14:50" (2笔交易)

User2位于" 10/2/2014 15:00"之间的station1和" 10/2/2014 15:15"(5笔交易)

user3在2014年10月3日16:31(1次交易)

上的station1上

User2在同一天再次出现在" 10/2/2014 17:04"和" 10/2/2014 17:06"(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




我正在寻找类似于&#34的输出;用户在电台上的时间长度以及有多少交易" ...是否可以在不迭代每个项目的情况下执行相同操作?如果是这样我该怎么办呢?



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




2 个答案:

答案 0 :(得分:0)

运行这些脚本

脚本1

{6, 15}

脚本2

-- =============================================
-- 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

脚本3

-- =============================================
-- 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

等待1或2天进行用户交易并运行

-- =============================================
-- 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 :(得分:0)

设置数据:

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 |
+----------+----------+-------------------------+----------+------------------+

如果帖子已回答问题

,请“标记为答案”