SQL group by frequency within a date range

后端 未结 4 1934
攒了一身酷
攒了一身酷 2020-12-31 16:44

I have a requirement to write a stored procedure that accepts a start date, end date and a frequency (day, week, month, quarter, year) and outputs a result set based on thos

相关标签:
4条回答
  • 2020-12-31 17:26

    The following script represents the output in a unified way: it shows period's start and end dates as well as the total count for the period.

    That has also determined the ways of finding the values to group by. Basically, you can see three distinct patterns: one for the 'day' frequency , another one for 'week' and still another for all the other frequency types.

    The first one is simplest: both PeriodStart and PeriodEnd are just Date.

    For weeks, I'm using a quite well known trick, whereby the first day of week is derived from the given date by subtracting from it a value that is one less than its weekday number. The end of the week is found similarly: we are merely adding 6 to the same expression.

    Months, quarters and years are grouped in the following manner. The integer number of corresponding units between the zero date and the given date is added back to the zero date. That gives us the beginning of the period. The end is found very similarly, only we are adding the number that is one greater than the difference. That produces the beginning of the next period, so we are then subtracting one day, which gives us the correct ending date.

    SELECT
      PeriodStart,
      PeriodEnd,
      Count = SUM(Count)
    FROM (
      SELECT
        PeriodStart = CASE @Frequency
          WHEN 'day'     THEN Date
          WHEN 'week'    THEN DATEADD(DAY, 1 - DATEPART(WEEKDAY, Date), Date)
          WHEN 'month'   THEN DATEADD(MONTH,   DATEDIFF(MONTH,   0, Date), 0)
          WHEN 'quarter' THEN DATEADD(QUARTER, DATEDIFF(QUARTER, 0, Date), 0)
          WHEN 'year'    THEN DATEADD(YEAR,    DATEDIFF(YEAR,    0, Date), 0)
        END,
        PeriodEnd   = CASE @Frequency
          WHEN 'day'     THEN Date
          WHEN 'week'    THEN DATEADD(DAY, 7 - DATEPART(WEEKDAY, Date), Date)
          WHEN 'month'   THEN DATEADD(DAY, -1, DATEADD(MONTH,   DATEDIFF(MONTH,   0, Date) + 1, 0))
          WHEN 'quarter' THEN DATEADD(DAY, -1, DATEADD(QUARTER, DATEDIFF(QUARTER, 0, Date) + 1, 0))
          WHEN 'year'    THEN DATEADD(DAY, -1, DATEADD(YEAR,    DATEDIFF(YEAR,    0, Date) + 1, 0))
        END,
        Count
      FROM atable
      WHERE Date BETWEEN @DateStart AND @DateEnd
    ) s
    GROUP BY
      PeriodStart,
      PeriodEnd
    
    • EXEC spReport '1/1/2011', '12/31/2011', 'day':

      PeriodStart PeriodEnd  Count
      ----------- ---------- -----
      2011-11-15  2011-11-15 6
      2011-12-16  2011-12-16 9
      2011-12-17  2011-12-17 2
      2011-12-18  2011-12-18 5
      
    • EXEC spReport '1/1/2011', '12/31/2011', 'week':

      PeriodStart PeriodEnd  Count
      ----------- ---------- -----
      2011-11-13  2011-11-19 6
      2011-12-11  2011-12-17 11
      2011-12-18  2011-12-24 5
      
    • EXEC spReport '1/1/2011', '12/31/2011', 'month':

      PeriodStart PeriodEnd  Count
      ----------- ---------- -----
      2011-11-01  2011-11-30 6
      2011-12-01  2011-12-31 16
      
    • EXEC spReport '1/1/2011', '12/31/2011', 'quarter':

      PeriodStart PeriodEnd  Count
      ----------- ---------- -----
      2011-10-01  2011-12-31 22
      
    • EXEC spReport '1/1/2011', '12/31/2011', 'year':

      PeriodStart PeriodEnd  Count
      ----------- ---------- -----
      2011-01-01  2011-12-31 22
      

    Note: From MSDN:

    Avoid the use of the sp_ prefix when naming procedures. This prefix is used by SQL Server to designate system procedures. Using the prefix can cause application code to break if there is a system procedure with the same name. For more information, see Designing Stored Procedures (Database Engine).

    0 讨论(0)
  • 2020-12-31 17:27

    Something like this should work.

    select date_column,
         sum(count)
    from @table
    where date_column between @start_date and @end_date
    group by case @frequency
                        when 'week' then datepart(week,date_column )
                        when 'year' then datepart(year,date_column)
                        when 'quarter' then datepart(quarter,date_column)
                        when ...
             end;
    
    0 讨论(0)
  • 2020-12-31 17:27
    select `date` as weekOf, sum(amt)
    from myTable
    where `date` between '2011-10-01' and '2012-01-01'
    group by week(`date` )
    
    0 讨论(0)
  • 2020-12-31 17:40
     Create procedure MyProc
     @startDate DateTime,
     @endDate DateTime,
     @freq varChar(5)
     As
    
        If @freq = "day" 
            Select DateAdd(day, 0, datediff(day, 0, date)), Frequency,
            Sum(Count) 
            From Table
            Group By DateAdd(day, 0, datediff(day, 0, date))
        Else If @freq = "week" 
            Select DateAdd(week, 0, datediff(week, 0, date)), Frequency,
            Sum(Count) 
            From Table
            Group By DateAdd(week, 0, datediff(week, 0, date))
        Else If @freq = "Month" 
            Select DateAdd(Month, 0, datediff(Month, 0, date)), Frequency,
            Sum(Count) 
            From Table
            Group By DateAdd(Month, 0, datediff(Month, 0, date))
        Else If @freq = "Quarter" 
            Select DateAdd(Quarter, 0, datediff(Quarter, 0, date)), Frequency,
            Sum(Count) 
            From Table
            Group By DateAdd(Quarter, 0, datediff(Quarter, 0, date))
        Else If @freq = "Year" 
            Select DateAdd(Year, 0, datediff(Year, 0, date)), Frequency,
            Sum(Count) 
            From Table
            Group By DateAdd(Year, 0, datediff(Year, 0, date))
    
    0 讨论(0)
提交回复
热议问题