【问题标题】:SQL Server: whole weeks total in a calendar monthSQL Server:一个日历月的整周总数
【发布时间】:2016-10-17 13:24:59
【问题描述】:

我想要一个月内的每周总计。它不包括任何部分星期或未来几周。每周从周一到周日开始。

我有一个像

这样的表结构
Date          Value      -- Comments
----------------------------------------------------------------------   
2016-10-01      7        Ignore this because its not a whole  week in a month
2016-10-05      8        Week 1  
2016-10-07      5        Week 1  
2016-10-11      2        Week 2  
2016-10-15      1        Week 2    
2016-10-17      9        Ignore this because the week is not finished yet

输出

 WeekNo         Total
    41             13
    42              3

【问题讨论】:

  • 您的预期输出是什么?只需添加
  • 整整一周是什么意思?
  • 例如:在当前日历月。我们将忽略 26Sep-2Oct 周和 31Oct-6Nov 周,因为它们不是当月的整周。
  • 这意味着来自任意日期范围的值将被忽略,即使您查询连续月份的数据......这是一个有趣的谜题,但在投入时间之前,我想质疑底层规范。这真的是一个有效的要求吗?

标签: sql sql-server


【解决方案1】:

更简单的方法是构建一个 Tally“日期”表。 您可以从任何 Tally Table 生成它,例如:

DECLARE @StartDate DATE = '20160101'
  , @EndDate DATE = '20161231';

WITH cte AS (
SELECT  DATEADD(DAY, n - 1, @StartDate) AS date
FROM    tally
WHERE   n - 1 <= DATEDIFF(DAY, @StartDate, @EndDate)
)
SELECT 
    c.date 
    ,YEAR(c.date) AS Year
    ,MONTH(c.date) AS Month
    ,DAY(c.date) AS Month
    ,DATEPART(WEEK,c.date) AS Week
    ,CASE WHEN 7<>COUNT(c.date) OVER (PARTITION BY YEAR(c.date),MONTH(c.date),DATEPART(WEEK,c.date)) THEN  0 ELSE 1 END AS isFullWeek
FROM cte c

然后你只需要将它加入到你需要的任何查询中。

【讨论】:

    【解决方案2】:
    DECLARE @StartDate datetime = '2011-10-01';
    DECLARE @EndDate datetime = '2016-10-31';
    SELECT
      CAST(DATEADD(dd, -DATEPART(dw, tblData.RecordDate) + 2, tblData.RecordDate) AS date) AS WeekStart,
      CAST(DATEADD(dd, -DATEPART(dw, tblData.RecordDate) + 8, tblData.RecordDate) AS date) AS WeekEnd,
      SUM(Value) AS Total
    FROM tblData
    
    WHERE (@StartDate IS NULL
    OR CAST(DATEADD(dd, -DATEPART(dw, tblData.RecordDate) + 2, tblData.RecordDate) AS date) >= CAST(@StartDate AS date))
    AND (@EndDate IS NULL
    OR CAST(DATEADD(dd, -DATEPART(dw, tblData.RecordDate) + 8, tblData.RecordDate) AS date) <= CAST(@EndDate AS date))
    AND CAST(DATEADD(dd, -DATEPART(dw, tblData.RecordDate) + 8, tblData.RecordDate) AS date) < CAST(GETDATE() AS date)
    GROUP BY CAST(DATEADD(dd, -DATEPART(dw, tblData.RecordDate) + 2, tblData.RecordDate) AS date),
             CAST(DATEADD(dd, -DATEPART(dw, tblData.RecordDate) + 8, tblData.RecordDate) AS date)
    

    【讨论】:

      【解决方案3】:

      创建一个满足您要求的日历表,如下所示:

          create table calendarTable ([date] date, weekNro int)
              go
      
          insert into  calendarTable 
          select dateadd(d,n,'20160101'), DATEPART(WK,dateadd(d,n,'20151231')) 
          from  numbers where n<500
      

      如果您没有数字表,则必须先创建它。像这样

      SET NOCOUNT ON    
      CREATE TABLE Numbers (n bigint PRIMARY KEY)    
      GO    
      DECLARE @numbers table(number int);  
      WITH numbers(number) as  (   
      SELECT 1 AS number   
      UNION all   
      SELECT number+1 FROM numbers WHERE number<10000  
      )  
      INSERT INTO @numbers(number)  
      SELECT number FROM numbers OPTION(maxrecursion 10000)
      INSERT INTO Numbers(n)  SELECT number FROM @numbers
      

      然后查询您的表加入日历表,记住完成一周的实际日期,如下所示:

      【讨论】:

        【解决方案4】:

        类似于@Kilren,但翻译成 postgres 并使用来自https://stackoverflow.com/a/11391987/10087503 的生成系列来生成日期

        DECLARE @StartDate DATE = '20160101'
          , @EndDate DATE = '20161231';
        
        WITH cte AS (
        SELECT i::date AS date FROM generate_series(@StartDate, 
          @EndDate, '1 day'::interval) i
        )
        SELECT 
            c.date 
            ,DATE_TRUNC('month' ,c.date) AS month_trunc
            ,DATE_PART('week',c.date) AS week
            ,CASE WHEN 7<>COUNT(c.date) 
            OVER (PARTITION BY DATE_TRUNC('month' ,c.date),DATE_PART('week',c.date)) 
            THEN  0 ELSE 1 END AS is_full_week
        FROM cte c
        

        【讨论】:

          【解决方案5】:
              Select DATEPART(ww, date) , SUM(Case When Comments Like '%1' then Value when Comments Like '%2' then Value else Value end)
          
          from schema.tablename
          
          group by DATEPART(ww,date)
          

          如果这不起作用,我很抱歉,这是我认为构建它的唯一方法。

          【讨论】:

          • 不清楚这将如何解决问题中描述的问题
          猜你喜欢
          • 1970-01-01
          • 2019-09-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-09-28
          • 1970-01-01
          相关资源
          最近更新 更多