2016-05-31 103 views
0

我試圖確定根據CreateDate場每個月的週末天數。數的週末每月

當月有31天的時候,我目前的努力似乎已經停止了1天。

任何人都可以解釋爲什麼這是怎麼回事?

;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

正如你可以看到突出顯示的數字在一個月不等於總天數做。

+1

的硬類跟隨你的查詢,但你有沒有考慮數着日子'WHERE DATEPART(平日,[YourDate])NOT IN(0,1)和'在你的月份組? – scsimon

+2

你應該建立一個date_dim表,這個表有周末標誌。這是我的最愛,但有一堆:[date_dim鏈接](https://www.mssqltips.com/sqlservertip/4054/creating-a-date-dimension-or-calendar-table-in-sql-server /) – Matt

回答

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

如果您要創建日曆表,爲什麼每個日期都沒有記錄?這將會更有用。 –

+0

@DanBracuk:因爲我想避免計算週日的數量。這種計算包括過濾日期列和IsWeekend上的行,然後計算COUNT。如果源表包含具有相同CreateDate的1000行,則〜1000倍的SQL Server將不得不計算並重新計算相同的值。 –

+1

感謝大家的意見和幫助,我最終選擇了一個日曆表格,並重新編寫了其餘的代碼以合併這個新表格。 –

0

這是一個格式化的評論。我將從兩個假設開始。首先是你沒有日曆表,即每個日期都有一行數據庫表。第二個是你的應用程序不一定每天創建記錄。基於這些假設,我提出以下方法。

  1. 從相關日期的年份和月份創建一個名爲@startDate的變量。使它成爲本月的第一個。

  2. 加一個月(使用DATEDIFF當然),以@startDate,然後一個減去每天做一個名爲@EndDate變量。

  3. 創建並填充表變量與所有那些兩個變量之間的日期。網上有很多關於如何做到這一點的例子。

  4. 在這個臨時表使用日期函數來得到你所需要的。

0

你可以使用一個「符合表」創建日期的日曆,從給定日起計算。在本例中,我使用了一個變量作爲開始日期。我有兩個CTE。第一個使用DATEADD函數從一個變量date開始計算一個Tally表並生成10,000個日期。使用DATEPART功能,我可以確定每個日期的MONTH,YEAR和WEEKDAY。

對於WeekendDays查詢(第一CTE),我只是得到WEEKDAY值是1或7(星期日或星期六)的計數。對於平日CTE,在底部,我做同樣的事情,但我排除是1或7

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] 
0

您可以使用下面的查詢WEEKDAY值。它也考慮閏年。

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