2016-12-03 291 views
2

(SQL Server 2008和以上):我需要更新dimDate表包括以下幾列:SQL Server的日期表:編程找到的最後一個星期日在一個特定的月份

  • 上週日的月份:
  • 上週一月:
  • 上週二月:
  • 上週三每月的:
  • 上週四月:
  • 上週五的一個月:
  • 上週六每月的:

不要誤會我的意思;這是非常簡單的,通過下面的代碼來定位該月的最後一天SQL:

CONVERT(DATE, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime))) AS [LastDayOfTheMonth] 

但我找不到任何地方在互聯網上,我承認不花2個多小時搜尋,那裏的人們公開展示瞭如何月份的識別最後(插天名稱)。

因此,我解決了這個方程,我在這裏張貼,如果它是對別人有用,或者,如果任何人有一個更簡單的方法,我可以使用,但根本無法看到它。

dimDates表通過預先填充dimNumbers表填充:

IF OBJECT_ID('dbo.dimNumbers') IS NOT NULL 
     DROP TABLE dbo.dimNumbers; 

DECLARE @UpperBound INT = 1000000; 

;WITH cteN(Number) AS 
(
    SELECT 
     ROW_NUMBER() OVER (ORDER BY s1.[object_id]) - 1 
    FROM 
     sys.all_columns AS s1 
    CROSS JOIN 
     sys.all_columns AS s2 
) 
SELECT [Number] 
INTO ref.dimNumbers 
FROM cteN 
WHERE [Number] <= @UpperBound; 

CREATE UNIQUE CLUSTERED INDEX CIX_dimNumbers ON ref.dimNumbers([Number]); 

然後被填充昏暗日期表通過以下的手段。是的,我很懶,希望SQL執行所有可能的計算。

DECLARE @YearsToPopulate INT = 130; 

-- Use the Magic of SQL to identify 1 Jan and then 31st December at the various edges of the required date time frames. 
DECLARE @StartDate DATE = DATEADD(yy, DATEDIFF(yy,0,DATEADD(yyyy,[email protected],GETDATE())), 0); 
DECLARE @EndDate DATE = DATEADD(yy, DATEDIFF(yy,0,DATEADD(yyyy,@YearsToPopulate,GETDATE())) + 1, -1); 

DECLARE @RecordsToCreate INT = DATEDIFF(dd,@StartDate,@EndDate); 

;WITH MyFullDateRange AS 
(
    SELECT TOP (@RecordsToCreate) 
     CAST(DATEADD(dd, Number, @StartDate) AS DATE) AS DayInTime 
    FROM 
     ref.[dimNumbers] 
) 
SELECT 
    --Insert Formulas here, using [DayInTime] as the Variable 
    -- The Formulas I have used here are not the topic of this discussion. 
FROM 
    MyFullDateRange 

所以,我花了幾個小時來證明,但我終於想出了一個簡單的,可重複的方式來發現「最後一個月(插天名稱)」。

NB:將上述SELECT語句內以下。

--Sunday [DayOfWeek] = 1.. Need to convert 1 to 0 <-> N + (7 - 1) % 7 
DATEADD(DD, 
     - ((DATEPART(dw, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime)))) + (7 - 1)) % 7 , 
     CONVERT(DATE, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime))) 
    ) 
    AS [LastsSundayOfTheMonth] 


--Monday [DayOfWeek] = 2.. Need to convert 2 to 0 <-> N + (7 - 2) % 7 
DATEADD(DD, 
      - ((DATEPART(dw, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime)))) + (7 - 2)) % 7 , 
      CONVERT(DATE, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime))) 
     ) 
     AS [LastMondayOfTheMonth] 


--Tuesday [DayOfWeek] = 3.. Need to convert 3 to 0 <-> N + (7 - 3) % 7 
DATEADD(DD, 
      - ((DATEPART(dw, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime)))) + (7 -3)) % 7 , 
      CONVERT(DATE, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime))) 
     ) 
     AS [LastTuesdayOfTheMonth] 

--Wednesday [DayOfWeek] = 4.. Need to convert 4 to 0 <-> N + (7 - 4) % 7 
DATEADD(DD, 
      - ((DATEPART(dw, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime)))) + (7 - 4)) % 7 , 
      CONVERT(DATE, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime))) 
     ) 
     AS [LastWednesdayOfTheMonth] 

--Thursday [DayOfWeek] = 5.. Need to convert 5 to 0 <-> N + (7 - 5) % 7 
DATEADD(DD, 
      - ((DATEPART(dw, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime)))) + (7 - 5)) % 7 , 
      CONVERT(DATE, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime))) 
     ) 
     AS [LastThursdayOfTheMonth] 

--Friday [DayOfWeek] = 6.. Need to convert 6 to 0 <-> N + (7 - 6) % 7 
DATEADD(DD, 
      - ((DATEPART(dw, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime)))) + (7-6)) % 7 , 
      CONVERT(DATE, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime))) 
     ) 
     AS [LastFridayOfTheMonth] 


--Saturday [DayOfWeek] = 7.. Need to convert 7 to 0 <-> N + (7 - 7) % 7 
DATEADD(DD, 
      - ((DATEPART(dw, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime)))) + (7-7)) % 7 , 
      CONVERT(DATE, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime))) 
     ) 
     AS [LastsSaturdayOfTheMonth] 

我希望這對他人有用,或者有人能夠指出一個更簡單的方法來執行這些操作。

要在[馬茨]答擴大爲SQL Server 2012:

下面的代碼適用於SQL Server 2012的及以上,並希望清楚地標識的每一天是如何識別。 (如果不是,讓我知道,我會澄清)

DECLARE @YearsToPopulate INT = 130; 

-- Use the Magic of SQL to identify 1 Jan and then 31st December at the various edges of the required date time frames. 
DECLARE @Date1 DATE = DATEADD(yy, DATEDIFF(yy,0,DATEADD(yyyy,[email protected],GETDATE())), 0); 
DECLARE @Date2 DATE = DATEADD(yy, DATEDIFF(yy,0,DATEADD(yyyy,@YearsToPopulate,GETDATE())) + 1, -1); 

DECLARE @RecordsToCreate INT = DATEDIFF(dd,@Date1,@Date2); 

WITH MyFullDateRange AS 
(
    SELECT TOP (@RecordsToCreate) CAST(DATEADD(dd, Number, @Date1) AS DATE) AS DayInTime 
    FROM ref.[dimNumbers] 
) 

SELECT DayInTime 
    --Sunday [DayOfWeek] = 1.. Need to convert 1 to 0 <-> N + (0 - 1) % 7 
    ,LastSundayOfMonth = DATEADD(DAY, 0 - (@@DATEFIRST - 1 + DATEPART(dw,EOMONTH(DayInTime))) % 7, EOMONTH(DayInTime)) 

    --Monday [DayOfWeek] = 2.. Need to convert 2 to 0 <-> N + (0 - 2) % 7 
    ,LastMondayOfMonth = DATEADD(DAY, 0 - (@@DATEFIRST - 2 + DATEPART(dw,EOMONTH(DayInTime))) % 7, EOMONTH(DayInTime)) 

    --Tuesday [DayOfWeek] = 3.. Need to convert 2 to 0 <-> N + (0 - 3) % 7 
    ,LastTuesdayOfMonth = DATEADD(DAY, 0 - (@@DATEFIRST - 3 + DATEPART(dw,EOMONTH(DayInTime))) % 7, EOMONTH(DayInTime)) 

    --Wednesday [DayOfWeek] = 4.. Need to convert 4 to 0 <-> N + (0 - 4) % 7 
    ,LastWednesdayOfMonth = DATEADD(DAY, 0 - (@@DATEFIRST - 4 + DATEPART(dw,EOMONTH(DayInTime))) % 7, EOMONTH(DayInTime)) 

    --Thursday [DayOfWeek] = 5.. Need to convert 2 to 0 <-> N + (0 - 5) % 7 
    ,LastThursdayOfMonth = DATEADD(DAY, 0 - (@@DATEFIRST - 5 + DATEPART(dw,EOMONTH(DayInTime))) % 7, EOMONTH(DayInTime)) 

    --Friday [DayOfWeek] = 6.. Need to convert 2 to 0 <-> N + (0 - 6) % 7 
    ,LastFridayOfMonth = DATEADD(DAY, 0 - (@@DATEFIRST - 6 + DATEPART(dw,EOMONTH(DayInTime))) % 7, EOMONTH(DayInTime)) 

    --Saturday [DayOfWeek] = 7.. Need to convert 2 to 0 <-> N + (0 - 7) % 7 
    ,LastSaturdayOfMonth = DATEADD(DAY, 0 - (@@DATEFIRST - 7 + DATEPART(dw,EOMONTH(DayInTime))) % 7, EOMONTH(DayInTime)) 

FROM MyFullDateRange; 

我將通過這個代碼,如果我沒有到支持SQL Server 2008

更多的SQL Server 2012開始碼:

不幸的是,我希望會是一個很好的選擇,是行不通的。請記住該解決方案需要適合dimDate表格。任何建議,以改善這一點?我不喜歡這種解決方案的一部分,我需要執行雙重前往dimDate表進行更新。

DECLARE @YearsToPopulate INT = 130; 

DECLARE @Date1 DATE = DATEADD(yy, DATEDIFF(yy,0,DATEADD(yyyy,[email protected],GETDATE())), 0); 
DECLARE @Date2 DATE = DATEADD(yy, DATEDIFF(yy,0,DATEADD(yyyy,@YearsToPopulate,GETDATE())) + 1, -1); 

DECLARE @RecordsToCreate INT = DATEDIFF(dd,@Date1,@Date2); 

WITH MyFullDateRange AS 
(
    SELECT TOP (@RecordsToCreate) CAST(DATEADD(dd, Number, @Date1) AS DATE) AS DayInTime 
    FROM ref.[dimNumbers] 
) 
, CreateListOfDatesAndDOWs As 
(
    Select DayInTime AS DayInTime, DatePART(DW , DayInTime) AS DayNumber 
    From MyFullDateRange 
) 

Select DayInTime AS [currentDate]--SQL 2012 -- DateFromParts(Year(DayInTime),Month(DayInTime) , 1) AS [currentDate] 
     ,LastSun = MAX(CASE WHEN DayNumber=1 THEN DayInTime END) 
     ,LastMon = MAX(CASE WHEN DayNumber=2 THEN DayInTime END) 
     ,LastTue = MAX(CASE WHEN DayNumber=3 THEN DayInTime END) 
     ,LastWed = MAX(CASE WHEN DayNumber=4 THEN DayInTime END) 
     ,LastThu = MAX(CASE WHEN DayNumber=5 THEN DayInTime END) 
     ,LastFri = MAX(CASE WHEN DayNumber=6 THEN DayInTime END) 
     ,LastSat = MAX(CASE WHEN DayNumber=7 THEN DayInTime END) 
From CreateListOfDatesAndDOWs 

Group By DayInTime --DateFromParts(Year(DayInTime),Month(DayInTime),1)--SQL 2012 

因爲這將返回下面的結果集,該結果集對於dimDate表而言並不是我們所追求的。 (我需要弄清楚如何格式化表!)SQL服務器2012+

DATEADD(DAY, 0 - (@@DATEFIRST - 1 + DATEPART(dw,EOMONTH(DateCol))) % 7, EOMONTH(DateCol)) 

這個公式將工作無論什麼DATEFIRST值設置(因爲使用EOMONTH()的)

currentDate LastSun LastMon LastTue LastWed LastThu LastFri LastSat 
----------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- 
1886-01-01 NULL  NULL  NULL  NULL  NULL  1886-01-01 NULL 
1886-01-02 NULL  NULL  NULL  NULL  NULL  NULL  1886-01-02 
1886-01-03 1886-01-03 NULL  NULL  NULL  NULL  NULL  NULL 
1886-01-04 NULL  1886-01-04 NULL  NULL  NULL  NULL  NULL 
1886-01-05 NULL  NULL  1886-01-05 NULL  NULL  NULL  NULL 
1886-01-06 NULL  NULL  NULL  1886-01-06 NULL  NULL  NULL 
1886-01-07 NULL  NULL  NULL  NULL  1886-01-07 NULL  NULL 
1886-01-08 NULL  NULL  NULL  NULL  NULL  1886-01-08 NULL 
1886-01-09 NULL  NULL  NULL  NULL  NULL  NULL  1886-01-09 
1886-01-10 1886-01-10 NULL  NULL  NULL  NULL  NULL  NULL 
1886-01-11 NULL  1886-01-11 NULL  NULL  NULL  NULL  NULL 
1886-01-12 NULL  NULL  1886-01-12 NULL  NULL  NULL  NULL 
1886-01-13 NULL  NULL  NULL  1886-01-13 NULL  NULL  NULL 
1886-01-14 NULL  NULL  NULL  NULL  1886-01-14 NULL  NULL 
1886-01-15 NULL  NULL  NULL  NULL  NULL  1886-01-15 NULL 
1886-01-16 NULL  NULL  NULL  NULL  NULL  NULL  1886-01-16 
1886-01-17 1886-01-17 NULL  NULL  NULL  NULL  NULL  NULL 
1886-01-18 NULL  1886-01-18 NULL  NULL  NULL  NULL  NULL 
1886-01-19 NULL  NULL  1886-01-19 NULL  NULL  NULL  NULL 
1886-01-20 NULL  NULL  NULL  1886-01-20 NULL  NULL  NULL 
1886-01-21 NULL  NULL  NULL  NULL  1886-01-21 NULL  NULL 
1886-01-22 NULL  NULL  NULL  NULL  NULL  1886-01-22 NULL 
1886-01-23 NULL  NULL  NULL  NULL  NULL  NULL  1886-01-23 
1886-01-24 1886-01-24 NULL  NULL  NULL  NULL  NULL  NULL 
1886-01-25 NULL  1886-01-25 NULL  NULL  NULL  NULL  NULL 
1886-01-26 NULL  NULL  1886-01-26 NULL  NULL  NULL  NULL 
1886-01-27 NULL  NULL  NULL  1886-01-27 NULL  NULL  NULL 
1886-01-28 NULL  NULL  NULL  NULL  1886-01-28 NULL  NULL 
1886-01-29 NULL  NULL  NULL  NULL  NULL  1886-01-29 NULL 
1886-01-30 NULL  NULL  NULL  NULL  NULL  NULL  1886-01-30 
1886-01-31 1886-01-31 NULL  NULL  NULL  NULL  NULL  NULL 
1886-02-01 NULL  1886-02-01 NULL  NULL  NULL  NULL  NULL 
1886-02-02 NULL  NULL  1886-02-02 NULL  NULL  NULL  NULL 
1886-02-03 NULL  NULL  NULL  1886-02-03 NULL  NULL  NULL 
1886-02-04 NULL  NULL  NULL  NULL  1886-02-04 NULL  NULL 
1886-02-05 NULL  NULL  NULL  NULL  NULL  1886-02-05 NULL 
1886-02-06 NULL  NULL  NULL  NULL  NULL  NULL  1886-02-06 
+0

請標記你的DBMS以及版本。 –

回答

0

至。

工作例子(鏈接顯示它的工作原理http://rextester.com/ZHJL66222):

DECLARE @Table AS TABLE (DateCol DATE) 
INSERT INTO @Table VALUES ('1/15/2016'),('2/15/2016'),('3/15/2016'), 
    ('4/15/2016'),('5/15/2016'),('6/15/2016'),('7/15/2016'),('8/15/2016'), 
    ('9/15/2016'),('10/15/2016'),('11/15/2016'),('12/15/2016') 


;WITH cte AS (
    SELECT 
     * 
     ,LastSundayOfMonth = DATEADD(DAY, 0 - (@@DATEFIRST - 1 + DATEPART(dw,EOMONTH(DateCol))) % 7, EOMONTH(DateCol)) 
    FROM 
     @Table 
) 

SELECT 
    * 
    ,CheckToEnsure = DATENAME(dw,LastSundayOfMonth) 
FROM 
    cte 

對於SQL 2008可以切換EOMONTH()到這樣的事情:

DATEADD(DAY, - DATEPART(DAY,DateCol),DATEADD(MONTH,1,DateCol)) 

在這一週的其他日子本可以適應爲以及檢查出這個其他答案:https://stackoverflow.com/a/40942693/5510627

+0

週日好。其他日子,星期一到星期六。 提出了一個很好的觀點,即我必須處理SQL 2008,因此我沒有使用EOMONTH。雖然你讓我意識到我也遺漏了我週日的代碼。 – Drooten

+0

基本上與您發現的模式相同(7 - 期望的DATEFIRST值-1)。我其實回答然後刪除了這個昨天,因爲我不知道這是一個很好的答案,因爲我也在工作日期昏暗:) http://stackoverflow.com/a/40942693/5510627 – Matt

+0

@Drooten現在你有我重新考慮我的日期dimm,並可能會返回添加一週的最後X天列。儘管我現在有145列,但......任何方式,你削減它的日期dimm是一種痛苦,尤其是當你去計算週數...... – Matt

1

這是另一個快速選項。這將返回每個月的最後一個工作日

使用ad-hoc tally表和窗口函數Row_Number()。

只是爲了好玩,你可以有另一場計數的第一,第二...的月,季的週一,年

編輯 - 添加了最終條件聚集

Declare @Date1 date = '2000-01-01' 
Declare @Date2 date = DateAdd(YY,130,@Date1) 
;with cte0(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)) 
    ,cteD(D) As (Select Top (DateDiff(DD,@Date1,@Date2)) cast(DateAdd(DD,Row_Number() over (Order By (Select NULL))-1,@Date1) as date) From cte0 N1, cte0 N2, cte0 N3, cte0 N4, cte0 N5, cte0 N6) -- 1 Million 
    ,cte  As (
        Select Date=D 
          ,DOW  = DateName(DW,D) 
          ,DOWPosNeg = Row_Number() over (Partition By Year(D),Month(D),DateName(DW,D) Order by D Desc) 
        From cteD 
       ) 
Select Date = DateFromParts(Year(Date),Month(Date),1) 
     ,LastSun = max(case when DOW='Sunday' then Date else null end) 
     ,LastMon = max(case when DOW='Monday' then Date else null end) 
     ,LastTue = max(case when DOW='Tuesday' then Date else null end) 
     ,LastWed = max(case when DOW='Wednesday' then Date else null end) 
     ,LastThu = max(case when DOW='Thursday' then Date else null end) 
     ,LastFri = max(case when DOW='Friday' then Date else null end) 
     ,LastSat = max(case when DOW='Saturday' then Date else null end) 
From cte 
Where DOWPosNeg = 1 
Group By DateFromParts(Year(Date),Month(Date),1) 
Order BY 1 

返回

enter image description here

+0

這是一個有趣的概念,但不適合作爲dimDate表的一部分。 – Drooten

+0

哦,我剛剛意識到你做了什麼。你只是計算了一個月的最後一天是什麼。這不是我所追求的(這是一個簡單的計算)。我想知道,對於每個可能的星期幾,星期一和星期二和星期三和星期四和星期五以及星期六和星期天,我需要了解DATE是什麼,但只有當月的最後一個星期六或當月的最後一個星期三。所以最少有7列代表那些週日。 – Drooten

+0

@Drooten我想你錯過了我想說明的。查看帶有條件聚合的更新答案..在這種情況下,1,560個月int 0.078 ms –

0

當我想到一個日期表時,它有關於預先計算日期的各種事情。例如,這裏有一個我颳起了剛纔這個問題:

create table dbo.dimDate (
    CalendarDate date not null primary key, 
    DateYear as year(CalendarDate) persisted, 
    DateMonth as month(CalendarDate) persisted, 
    DateDay as day(CalendarDate) persisted, 
    DateDOW tinyint not null 
); 

無論你想要做的所有的東西與持久列...給你。但我的觀點是,他們在那裏作爲數據,而不是在運行時計算。一旦你有像上述情況,你可以得到你要查找的內容與下面的查詢:

with cte as (
    select *, 
     row_number() over (
      partition by DateYear, DateMonth, DateDow 
      order by CalendarDate desc 
     ) as RankedDow 
    from dbo.dimDate 
) 
select * 
from cte 
where RankedDow = 1 
order by CalendarDate; 

而且,在持續數據的精神,我會做的dimDate列新列(有點像LastDOWOfMonth)並堅持以上的結果!然後,你可以在dimDate上做一個簡單的查詢,如

select CalendarDate 
from dbo.dimDate 
where DateYear = 2016 
    and DateMonth = 12 
    and DOW = 5 
    and LasDOWOfMonth = 1; 
+0

是的我認爲每個人都已經閱讀了Kimball的數據倉庫書籍,瞭解dimTable的邏輯。 – Drooten

相關問題