【问题标题】:Group sales data by time interval for every 15 min for one day一天内每 15 分钟按时间间隔分组销售数据
【发布时间】:2017-10-31 11:42:29
【问题描述】:

我有一个销售表,它包含不同商店的销售数据以及时间,假设在一天中,我们已经完成了 10,000 笔交易,那么我需要每 15 分钟找到该特定业务的总销售额日期,例如:如果在 12:00 PM 到 12:15 PM 之间没有销售,那么它应该是零值或 null。

一天有 24 小时,所以这意味着 15 分钟间隔有 96 列。

Sales Table:

SiteName          Time          Amount        BusinessDate
----------------------------------------------------------
A                7:01:02 AM     20            2017-01-02
A                7:03:22 AM     25            2017-01-02
A                7:05:03 AM     33            2017-01-02
A                7:11:02 AM     55            2017-01-02
A                7:13:05 AM     46            2017-01-02
A                7:17:02 AM     21            2017-01-02
A                8:01:52 AM     18            2017-01-02
A                8:55:42 AM     7             2017-01-02
A                8:56:33 AM     7             2017-01-02
A                8:58:55 AM     31            2017-01-02

等等

我怎样才能做到这一点?!

【问题讨论】:

  • 我会为此使用一个计数表。如果您需要更多详细信息,您需要将它们提供给我们。这是一个很好的起点。 spaghettidba.com/2015/04/24/…
  • 您需要在 CTE 中预先或作为递归查询创建时间间隔表,然后加入它。
  • @PM77-1 如果您能给我提供一个高级语法来确定如何开始,我将不胜感激
  • 您在表格中没有与时间关联的date 吗?
  • @SeanLange 我提供了关于我的表格的示例数据

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


【解决方案1】:

动态示例

Declare @SQL varchar(max) = Stuff((Select ',' + QuoteName(T) 
                                    From (Select Top 96 T=format(DateAdd(Minute,(Row_Number() Over (Order By (Select null))-1)*15,0),'HH:mm') From  master..spt_values n1) A
                                    Order by 1 
                                    For XML Path('')),1,1,'') 
Select  @SQL = '
Select *
From (
        Select [SiteName]
              ,Col   = format(DateAdd(MINUTE,(DatePart(HOUR,[Time])*60) + ((DatePart(MINUTE,[Time]) / 15)*15),0),''HH:mm'')
              ,Value = [Amount]
         From  Sales 
     ) A
Pivot (sum(Value) For [Col] in (' + @SQL + ') ) p'
Exec(@SQL);

从 00:00 到 23:45 返回 96 列

生成的代码

Select *
From (
        Select [SiteName]
              ,Col   = format(DateAdd(MINUTE,(DatePart(HOUR,[Time])*60) + ((DatePart(MINUTE,[Time]) / 15)*15),0),'HH:mm')
              ,Value = [Amount]
         From  Sales 
     ) A
Pivot (sum(Value) For [Col] in ([00:00],[00:15],[00:30],[00:45],[01:00],[01:15],[01:30],[01:45],[02:00],[02:15],[02:30],[02:45],[03:00],[03:15],[03:30],[03:45],[04:00],[04:15],[04:30],[04:45],[05:00],[05:15],[05:30],[05:45],[06:00],[06:15],[06:30],[06:45],[07:00],[07:15],[07:30],[07:45],[08:00],[08:15],[08:30],[08:45],[09:00],[09:15],[09:30],[09:45],[10:00],[10:15],[10:30],[10:45],[11:00],[11:15],[11:30],[11:45],[12:00],[12:15],[12:30],[12:45],[13:00],[13:15],[13:30],[13:45],[14:00],[14:15],[14:30],[14:45],[15:00],[15:15],[15:30],[15:45],[16:00],[16:15],[16:30],[16:45],[17:00],[17:15],[17:30],[17:45],[18:00],[18:15],[18:30],[18:45],[19:00],[19:15],[19:30],[19:45],[20:00],[20:15],[20:30],[20:45],[21:00],[21:15],[21:30],[21:45],[22:00],[22:15],[22:30],[22:45],[23:00],[23:15],[23:30],[23:45]) ) p

【讨论】:

    【解决方案2】:

    这是一个不使用动态 SQL 的选项,而不是每行 96 列宽,而是在每个时隙生成一行。首先,我从您的数据示例表开始。

    create table #Sales
    ( SiteName nvarchar(1),
        SaleTime time,
        Amount decimal,
        BusinessDate Date );
    
    insert into #Sales ( SiteName, SaleTime, Amount, BusinessDate )
    values 
    ( 'A', '7:01:02', 20, '2017-01-02' ),
    ( 'A', '7:03:22', 25, '2017-01-02' ),
    ( 'A', '7:05:03', 33, '2017-01-02' ),
    ( 'A', '7:11:02', 55, '2017-01-02' ),
    ( 'A', '7:13:05', 46, '2017-01-02' ),
    ( 'A', '7:17:02', 21, '2017-01-02' ),
    ( 'A', '8:01:52', 18, '2017-01-02' ),
    ( 'A', '8:55:42', 7, '2017-01-02' ),
    ( 'A', '8:56:33', 7, '2017-01-02' ),
    ( 'A', '8:58:55', 31, '2017-01-02' );
    
    And the query which I will explain shortly
    
    select
            allTimes.TimeStart,
            allTimes.TimeEnd,
            coalesce( count(S.Amount), 0 ) as NumEntries,
            coalesce( sum( S.Amount), 0 ) as SumValues
        from
            ( select
                    cast( DateAdd( minute, 15 * (timeSlots.Row -1), '2017-01-01' ) as time ) as TimeStart,
                    cast( DateAdd( minute, 15 * timeSlots.Row, '2017-01-01' ) as time ) as TimeEnd
                from
                    ( SELECT top 96
                            ROW_NUMBER() OVER(Order by AnyColumnInYourTable) Row
                        FROM     
                            AnyTableThatHasAtLeast96Rows ) timeSlots
            ) allTimes
                LEFT JOIN #Sales S
                    on allTimes.TimeStart <= S.SaleTime
                    AND S.SaleTime < allTimes.TimeEnd
                AND ( allTimes.TimeEnd < allTimes.TimeStart
                    OR S.SaleTime <= allTimes.TimeEnd )
        group by
            allTimes.TimeStart,
            allTimes.TimeEnd
    

    现在,解释...

    首先,最里面的查询别名结果“timeSlots”。这可以从任何至少有 96 个时隙 15 分钟增量的表中查询,并且只返回一个从 1 到 96 顺序编号的结果集。

    现在我们有 96 行,我们得到下一个外部查询别名结果“allTimes”。这基本上进行日期/时间数学运算,并添加 15 分钟间隔 * 无论“行”数值是什么,都将所有时隙创建为 96 行。我已经明确应用了开始和结束时间来应用 >= 和

    TimeStart    TimeEnd
    00:00:00     00:15:00
    00:15:00     00:30:00
    00:30:00     00:45:00
    ...
    23:30:00     23:45:00
    23:45:00     00:00:00   -- This one is special for the JOIN clause for time
    

    现在,左连接......这是一个有点棘手的

    LEFT JOIN #Sales S
        on allTimes.TimeStart <= S.SaleTime
        AND S.SaleTime < allTimes.TimeEnd
        AND ( allTimes.TimeEnd < allTimes.TimeStart
            OR S.SaleTime <= allTimes.TimeEnd )
    

    在这里,左加入销售将始终允许每个时间段都在最终结果集中。但是,给定的销售适合哪个时段?销售时间必须大于或等于开始的 15 分钟间隔...

    和..

    要么...结束时间小于开始时间(通过时间段在第二天早上 23:45 - 00:00),或者小于下一个时间段的开始时间。例如:08:30 - 8:45 时隙实际上达到 8:44:xxxxx 精度,但始终小于 8:45。

    通过在每个时间段使用一行,我可以获得交易计数、交易总和,您甚至还可以计算销售活动的平均值、最小值、最大值,以发现趋势。

    【讨论】:

      【解决方案3】:
       WITH dates AS (
       SELECT CAST('2009-01-01' AS datetime) 'date'
       UNION ALL
       SELECT DATEADD(mi, 15, t.date) 
         FROM dates t
        WHERE DATEADD(mi, 15, t.date) < '2009-01-02')
       SELECT cast([date] as time) as [date] from dates
      

      使用上面的代码可以得到一天 15 分钟间隔的 96 列。

      使用上述 CTE 加入 sales 表。

      【讨论】:

        【解决方案4】:

        这里的递归 CTE 生成 24 小时的 15 分钟间隔(96 行)。

        然后这个结果LEFT JOINed 到子查询。在子查询中,Amount 每小时按 15 分钟间隔分组。

        结果,00:00:00 对应于从00:00:0000:14:59 发生的金额总和

        00:15:00 = 从00:15:0000:29:59

        00:30:00 = 从00:30:0000:44:59

        00:45:00 = 从00:45:0000:59:59

        每 24 小时以此类推

        create table #Sales
        ( SiteName nvarchar(1),
            SaleTime time,
            Amount decimal,
            BusinessDate Date );
        
        insert into #Sales ( SiteName, SaleTime, Amount, BusinessDate )
        values 
        ( 'A', '13:22:36', 888, '2017-01-02' ),
        ( 'A', '00:00:00', 20, '2017-01-02' ),
        ( 'A', '00:00:00', 30, '2017-01-02' ),
        ( 'A', '00:45:00', 88, '2017-01-02' ),
        ( 'A', '12:46:05', 22, '2017-01-02' ),
        ( 'A', '12:59:59', 22, '2017-01-02' ),
        ( 'A', '23:59:59', 10, '2017-01-02' );
        
        -- Below is actual query:
        
        with rec as(
            select cast('00:00:00' as time) as dt
            union all
            select DATEADD (mi , 15 , dt)  from rec 
            where
            dt < cast('23:45:00' as time)  
        )
        select rec.dt, t1.summ from rec
        left join 
        (select part, sum(Amount) as summ from (
            select *, case 
                when DATEPART ( mi , SaleTime ) < 15 then concat(SUBSTRING (cast(SaleTime as varchar) ,1 , 2 ), ':00:00')
                when DATEPART ( mi , SaleTime ) between 15 and 29 then concat(SUBSTRING (cast(SaleTime as varchar) ,1 , 2 ), ':15:00') 
                when DATEPART ( mi , SaleTime ) between 30 and 44 then concat(SUBSTRING (cast(SaleTime as varchar) ,1 , 2 ), ':30:00') 
                else concat(SUBSTRING (cast(SaleTime as varchar) ,1 , 2 ), ':45:00') 
                end as part
            from #Sales
            where BusinessDate = '2017-01-02'
        ) t
        group by part) t1
        on rec.dt = t1.part
        order by rec.dt
        

        rextester demo

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-08-18
          • 2021-07-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多