每月的周末天数

时间:2016-05-31 19:26:40

标签: sql sql-server

我正在尝试根据CreateDate字段确定每个月的周末天数。

我当前的尝试似乎在一个月内有31天的时候停止了一天。

任何人都可以解释为什么会这样吗?

;WITH cteNetProfit
AS (
---- NET PROFIT
SELECT    DT.CreateDate
       , SUM(DT.Revenue) AS Revenue
       , SUM(DT.Cost) AS Cost
       , SUM(DT.GROSSPROFIT) AS GROSSPROFIT
FROM
        (
        SELECT CAST([createDTG] AS DATE) AS CreateDate
            , SUM(Revenue) AS Revenue
            , SUM(Cost) AS Cost
            , SUM(REVENUE - COST) AS GROSSPROFIT
        FROM     [dbo].[CostRevenueSpecific]
        WHERE   CAST([createDTG] AS DATE) > CAST(GETDATE() - 91 AS DATE)
               AND CAST([createDTG] AS DATE) < = CAST(GETDATE() - 1 AS DATE)
        GROUP BY createDTG
        UNION ALL
        SELECT CAST([CallDate] AS DATE) AS CreateDate
            , SUM(Revenue) AS Revenue
            , SUM(Cost) AS Cost
            , SUM(REVENUE - COST) AS GROSSPROFIT
        FROM   [dbo].PublisherCallByDay
        WHERE  CAST([CallDate] AS DATE) > CAST(GETDATE() - 91 AS DATE)
              AND CAST([CallDate] AS DATE) <= CAST(GETDATE() - 1 AS DATE)
        GROUP BY CALLDATE) DT
GROUP BY DT.CreateDate),
A1
AS (SELECT DISTINCT
         MONTH(CREATEDATE) AS MONTHNO
        , DAY(EOMONTH(CreateDate)) AS DaysinMth
    FROM   cteNetProfit),
A2
AS (
SELECT A.MONTHNO
    , COUNT(A.WorkDay) AS DAYSPERMTH
    , A.WorkDay
FROM
        (SELECT MONTH(CREATEDATE) AS MONTHNO
             , CHOOSE
               (DATEPART(dw, CreateDate), 'WEEKEND', 'Weekday', 'Weekday', 'Weekday', 'Weekday', 'Weekday', 'WEEKEND') AS WorkDay
         FROM   cteNetProfit) A
WHERE   A.WORKDAY = 'WEEKEND'
GROUP BY A.MONTHNO
      , A.WorkDay
UNION
SELECT A.MONTHNO
    , COUNT(A.WorkDay) AS DAYSPERMTH
    , A.WorkDay
FROM
      (SELECT MONTH(CREATEDATE) AS MONTHNO
           , CHOOSE
            (DATEPART(dw, CreateDate), 'WEEKEND', 'Weekday', 'Weekday', 'Weekday', 'Weekday', 'Weekday', 'WEEKEND') AS WorkDay
       FROM   cteNetProfit) A
WHERE  A.WORKDAY = 'WEEKDAY'
GROUP BY A.MONTHNO
      , A.WorkDay),
A3
AS (SELECT A1.MONTHNO
        , A1.DaysinMth
        , A2.DAYSPERMTH
        , A2.WorkDay
        , CASE
            WHEN A2.WorkDay = 'WEEKEND' THEN SUM(A2.DAYSPERMTH) / 2
            ELSE A2.DAYSPERMTH
         END AS DP_REV
    FROM   a1
         INNER JOIN A2 ON A1.MONTHNO = A2.MONTHNO
    GROUP BY A1.MONTHNO
         , A1.DaysinMth
         , A2.DAYSPERMTH
         , A2.WorkDay),
A4
AS (SELECT A3.MONTHNO
        , A3.DAYSINMTH
        , SUM(A3.DP_REV) AS DPREV
    --  , A3.DP_REV
    FROM   A1
         INNER JOIN A3 ON A1.MONTHNO = A3.MONTHNO
    GROUP BY A3.MONTHNO
         , A3.DaysinMth)
SELECT A1.MONTHNO
    , A1.DAYSINMTH
    , A4.DPREV
    , A3.DP_REV
    , A3.DAYSPERMTH
FROM   A1
    , A3
    , A4
WHERE  A1.MONTHNO = A4.MONTHNO
      AND A1.MONTHNO = A3.MONTHNO;

Sample

正如您所看到的,突出显示的数字与月份的总天数不相等。

4 个答案:

答案 0 :(得分:0)

这是一种格式化的评论。我将从两个假设开始。第一个是您没有日历表,即每个日期都有一行的数据库表。第二,您的应用程序不一定每天都会创建记录。基于这些假设,我建议采用以下方法。

  1. 从相关日期的年份和月份创建名为@startDate的变量。将它作为本月的第一天。

  2. 通过在@startDate中添加一个月(当然使用datediff)然后减去一天来创建一个名为@endDate的变量。

  3. 创建并填充表变量,其中包含这两个变量之间的所有日期。网上有很多关于如何做到这一点的例子。

  4. 在此临时表上使用日期函数来获取所需内容。

答案 1 :(得分:0)

最简单的解决方案之一是创建一个Calendar表来存储月份列表及其天数和周末天数:

CREATE TABLE Calendar.Month (
    YearMonth INT PRIMARY KEY NONCLUSTERED,
    Year SMALLINT NOT NULL,
    Month TINYINT NOT NULL,
    UNIQUE CLUSTERED (Year, Month),
    NumDays TINYINT NOT NULL CHECK(NumDays BETWEEN 28 AND 31),
    WeekendDays TINYINT NOT NULL CHECK(WeekendDays > 0),
    CHECK (WeekendDays < NumDays),
    NonWeekendDays AS (DaysNum - WeekendDays)
) 
-- Plus INSERT INTO Calendar.Month (...) VALUES (...) statements

然后我们必须使用简单的连接来获取所需的dataL

SELECT ..., m.WeekendDays
FROM dbo.SourceTable s
INNER JOIN Calendar.Month m ON m.Year = YEAR(s.CreateDate) AND m.Month = MONTH(s.CreateDate)
-- Above join predicate it's not so nice but performance should be good because of UNIQUE CLUSTERED index defined on Year and Month columns

答案 2 :(得分:0)

您可以使用“计数表”创建日期日历,从给定日期开始。我在这个例子中使用了一个变量作为开始日期。我有两个CTE。第一个采用计数表并使用DATEADD函数生成10,000个日期,从变量日期开始。使用DATEPART功能,我可以确定每个日期的MONTH,YEAR和WEEKDAY。

对于WeekendDays查询(第一次CTE),我只得到WEEKDAY值的计数,即1或7(星期日或星期六)。对于WeekDays CTE,在底部,我做同样的事情,但我排除了1或7的WEEKDAY值。

DECLARE @StartingDate DATE

SET @StartingDate = '2015-01-01'

;WITH TallyTable AS
(
    SELECT TOP 10000 ROW_NUMBER() OVER(ORDER BY a.id) AS N
    FROM Master.dbo.SysColumns a, Master.dbo.SysColumns b

)
SELECT DATEPART(YEAR,DATEADD(DAY, N - 1, @StartingDate)) AS [Year], DATEPART(MONTH,DATEADD(DAY, N - 1, @StartingDate)) AS [MONTH]
,COUNT(DATEPART(dw,DATEADD(DAY, N - 1, @StartingDate))) AS [WeekendDays]
FROM TallyTable
WHERE DATEPART(dw,DATEADD(DAY, N - 1, @StartingDate))  IN(7,1)
GROUP BY DATEPART(YEAR,DATEADD(DAY, N - 1, @StartingDate)), DATEPART(MONTH,DATEADD(DAY, N - 1, @StartingDate))
ORDER BY [Year], [MONTH]


;WITH TallyTable AS
(
    SELECT TOP 10000 ROW_NUMBER() OVER(ORDER BY a.id) AS N
    FROM Master.dbo.SysColumns a, Master.dbo.SysColumns b

)
SELECT DATEPART(YEAR,DATEADD(DAY, N - 1, @StartingDate)) AS [Year], DATEPART(MONTH,DATEADD(DAY, N - 1, @StartingDate)) AS [MONTH]
,COUNT(DATEPART(dw,DATEADD(DAY, N - 1, @StartingDate))) AS [WeekendDays]
FROM TallyTable
WHERE DATEPART(dw,DATEADD(DAY, N - 1, @StartingDate))  NOT IN(7,1)
GROUP BY DATEPART(YEAR,DATEADD(DAY, N - 1, @StartingDate)), DATEPART(MONTH,DATEADD(DAY, N - 1, @StartingDate))
ORDER BY [Year], [MONTH]

答案 3 :(得分:0)

您可以使用以下查询。它也考虑闰年。

with cte as (
select top (IIF(DATEPART(dd,(EOMONTH(CONCAT(2016,'0201')))) = 29, 366, 365)) 
    ROW_NUMBER() over (order by column_id) X
from sys.columns
)
, ds as (
select dateadd(day, X - 1, DATEFROMPARTS(2016, 1, 1)) dt
from cte
)
, W as (
select dt, DATEPART(w, dt) WD, DATEPART(month, dt) Mon
from ds
where DATEPART(w, dt) in (1, 7)
)
select Mon TheMonth, ceiling(1.0*COUNT(*)/2) AS NumberOfWeekEnds
from W
group by Mon

<强>结果

TheMonth    NumberOfWeekEnds
1           5
2           4
3           4
4           5
5           5
6           4
7           5
8           4
9           4
10          5
11          4
12          5