2012-07-03 33 views
1

的时间是:(m/d/yyyy) => 2009/01/04应用周数日期全周仅

enter image description here

使用此命令using datepart(wk,'20090104')我能得到周数(对于任何给定的日期)。

所以:

SELECT datepart(wk,'20090101') //1 
SELECT datepart(wk,'20090102') //1 
SELECT datepart(wk,'20090103') //1 
SELECT datepart(wk,'20090104') //2 

到目前为止好。

问题:

那些3的第一次约会是不是一个完整的星期的一部分,所以我不能把他们固定的52周图表英寸

我们公司需要了解每个整个周在52周一年的信息。 (每年有52个整周)。

enter image description here

所以20090101不属于2009年的第一个星期!

它属于(这是不相关的我的问题

所以我需要一个UDF上年(我一直在寻找了很多,ISOWEEK没有回答我的需求),它由一个给定的datetime,将给我Week Number(周=整周,所以不考虑部分周)。

例子:

calcweekNum ('20090101') //52 ...from the last year 
calcweekNum ('20090102') //52 ...from the last year 
calcweekNum ('20090103') //52 ...from the last year 
calcweekNum ('20090104') //1 
.. 
.. 
calcweekNum ('20090110') //1 
calcweekNum ('20090111') //2 
calcweekNum ('20090112') //2 
... 
+0

@marc_s再次感谢:)^5 –

+0

你有没有签出[我的回答](http://stackoverflow.com/a/11320441/57611)? – ErikE

回答

5

这是一种不同的方法。所有您需要提供的是一年:

DECLARE @year INT = 2009; 


DECLARE @start SMALLDATETIME; 
SET @start = DATEADD(YEAR, @year-1900, 0); 

;WITH n AS 
(
    SELECT TOP (366) -- in case of leap year 
     d = DATEADD(DAY, ROW_NUMBER() OVER (ORDER BY name)-1, @start) 
    FROM sys.all_objects 
), 
x AS 
(
    SELECT md = MIN(d) FROM n 
    WHERE DATEPART(WEEKDAY, d) = 1 -- assuming DATEFIRST is Sunday 
), 
y(d,wk) AS 
(
    SELECT n.d, ((DATEPART(DAYOFYEAR, n.d) - DATEDIFF(DAY, @start, x.md)-1)/7) + 1 
    FROM n CROSS JOIN x 
    WHERE n.d >= x.md 
    AND n.d < DATEADD(YEAR, 1, @start) 
) 
SELECT [date] = d, [week] = wk 
FROM y WHERE wk < 53 
ORDER BY [date]; 

结果:

date  week 
---------- ---- 
2009-01-04 1 
2009-01-05 1 
2009-01-06 1 
2009-01-07 1 
2009-01-08 1 
2009-01-09 1 
2009-01-10 1 
2009-01-11 2 
2009-01-12 2 
... 
2009-12-25 51 
2009-12-26 51 
2009-12-27 52 
2009-12-28 52 
2009-12-29 52 
2009-12-30 52 
2009-12-31 52 

需要注意的是第52周的不一定是一整个星期,而且在某些情况下(如2012),最后一年中的一两天可能会在第53周下降,因此它们被排除在外。

另一种方法是重复两次MIN表示:

DECLARE @year INT = 2009; 


DECLARE @start SMALLDATETIME; 
SET @start = DATEADD(YEAR, @year-1900, 0); 

;WITH n AS 
(
    SELECT TOP (366) -- in case of leap year 
     d = DATEADD(DAY, ROW_NUMBER() OVER (ORDER BY name)-1, @start) 
    FROM sys.all_objects 
), 
y(d,wk) AS 
(
    SELECT n.d, ((DATEPART(DAYOFYEAR, n.d) - DATEDIFF(DAY, @start, (SELECT MIN(d) 
    FROM n WHERE DATEPART(WEEKDAY, d) = 1))-1)/7) + 1 
    FROM n 
    WHERE n.d >= (SELECT md = MIN(d) FROM n WHERE DATEPART(WEEKDAY, d) = 1) 
    AND n.d < DATEADD(YEAR, 1, @start) 
) 
SELECT [date] = d, [week] = wk 
FROM y WHERE wk < 53 
ORDER BY d; 
+1

H ***你是怎么做到的?告诉我你已经有过...... –

+1

@RoyiNamir不,我刚刚写了很长一段时间的T-SQL。 –

+1

+1 - 很好的答案。当我看到这个时,我总是很感兴趣:'DATEADD(YEAR,somevalue,0)'然后我记得它是如何工作的:-) – Lamak

-1

没有很好的答案这一点。

一年不是52周。

这是52周,在正常年份之一天,和52周两天闰年。

+0

我可以处理那些小偏见。但是当一家公司想要看52周时 - 我必须将每个日期(有点偏见)调整到[1..52]周 –

+0

因此,公司并不关心年度是否在星期一开始,您完全忽略了前6天?也许不遵循一个日历周是一种更好的方法,比假装在第一周发生的一切发生在第一周,发生在第三周的一切发生在第二天等 –

+0

@AaronBertrand no。:)有52个数字Xaxis。 52. –

2

对于一年中的每个日期设置一行自定义日历表会比较容易,然后让其他字段允许您根据需要汇总。当我有客户使用不同的日历(即会计年度)时,我会这样做,并且它使查询逻辑非常简单。

然后,您只需加入日期和日期并获取所需的周数。

date  | reporting year | reporting week 
-----------|----------------|--------------- 
2009-01-01 | 2008   | 52 
2009-01-02 | 2008   | 52 
2009-01-03 | 2008   | 52 
2009-01-04 | 2009   | 01 
2009-01-05 | 2009   | 01 
etc. 

,然后使用它(例如,通过自定义几个星期才能总销售额汇总,并没有验证我的SQL):

select reporting_year, reporting_month, sum(sales) 
    from sales 
    inner join custom_date_table cdt on cdt.sysdate = sales.sysdate 
    group by reporting_year, reporting_month 
    where report_year=2009 
+0

所以表格将有大约365条记录? –

+0

是的,你每年增加一次(或者如果事先知道的话,可以一次设置多年)。这是最灵活的解决方案,不仅适用于您的案例,还适用于其他涉及自定义日期汇总的其他解决方案。 –

+0

第一个值是什么?你能提供3条线吗? –

0

不要使用UDF,使用calendar table相反,您可以按照您的公司要求精确定义周数,并从表中简单地查询它们,这比使用UDF更容易且可能快得多。

日历表在SQL(搜索本网站或Google)中有许多用途,因此无论如何您应该可以创建它。

1
DECLARE @StartDate DATE; 
SET @StartDate = '20120101'; 

WITH Calendar AS (
    SELECT @StartDate AS DateValue 
     ,DATEPART(DW, @StartDate) AS DayOfWeek 
     ,CASE WHEN DATEPART(DW, @StartDate) = 1 THEN 1 ELSE 0 END AS WeekNumber 
    UNION ALL 
    SELECT DATEADD(d, 1, DateValue) 
     ,DATEPART(DW, DATEADD(d, 1, DateValue)) AS DayOfWeek 
     ,CASE WHEN DayOfWeek = 7 THEN WeekNumber + 1 ELSE WeekNumber END 
    FROM Calendar 
    WHERE DATEPART(YEAR, DateValue) = DATEPART(YEAR, @StartDate) 
) 
SELECT DateValue, WeekNumber 
FROM Calendar 
WHERE WeekNumber BETWEEN 1 AND 52 
    AND DATEPART(YEAR, DateValue) = DATEPART(YEAR, @StartDate) 
OPTION (MAXRECURSION 0); 
+0

将非常优秀。 (所以它会给我一个原子年的信息)。请你详细说明/ –

+0

查看最新的答案。你是否想要包括下一年进入第52周的日期?让我知道,我会更新答案。 –

+0

不......你的意思是'20120101'输入中的最后一行吗?那里有53周...它应该被显示 –

3

这里有一个功能,让你计算它的飞行:

CREATE FUNCTION dbo.WholeWeekFromDate (
    @Date datetime 
) 
RETURNS tinyint 
AS BEGIN 
RETURN (
    SELECT DateDiff(Day, DateAdd(Year, DateDiff(Year, 0, CalcDate), 0), CalcDate)/7 + 1 
    FROM (SELECT DateAdd(Day, (DateDiff(Day, 0, @Date) + 1)/7 * 7, 0)) X (CalcDate) 
); 
END; 

我不建议你使用它,因为它可能因每行被调用一次而表现不佳。如果你绝对必须有一个功能,在实际的查询使用,然后将其转换为一个内联函数返回单个列和行,并把它作为这样:

SELECT 
    OtherColumns, 
    (SELECT WeekNumber FROM dbo.WholeWeekFromDate(DateColumn)) WeekNumber 
FROM 
    YourTable; 

这将允许它被“内联”执行计划和表现明显更好。

但正如其他人所建议的,更好的办法是使用BusinessDate表。这里有一个良好的开端创造一个适合你:

CREATE TABLE dbo.BusinessDate (
    BusinessDate date NOT NULL CONSTRAINT PK_BusinessDate PRIMARY KEY CLUSTERED, 
    WholeWeekYear smallint NOT NULL 
     CONSTRAINT CK_BusinessDate_WholeWeekYear_Valid 
     CHECK (WholeWeekYear BETWEEN 1900 AND 9999), 
    WholeWeekNumber tinyint NOT NULL 
     CONSTRAINT CK_BusinessDate_WholeWeekNumber_Valid 
     CHECK (WholeWeekNumber BETWEEN 1 AND 53), 
    Holiday bit CONSTRAINT DF_BusinessDate_Holiday DEFAULT (0), 
    Weekend bit CONSTRAINT DF_BusinessDate_Weekend DEFAULT (0), 
    BusinessDay AS 
     (Convert(bit, CASE WHEN Holiday = 0 AND Weekend = 0 THEN 1 ELSE 0 END)) PERSISTED 
); 

我会甚至从1900-01-01至2617年9月22日来填充它(这就够了你的产品的预期寿命和它的?只有7.8MB所以不要过度担心大小):

WITH A (N) AS (SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 
    UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1), 
B (N) AS (SELECT 1 FROM A F, A A, A L, A C, A O, A N), 
C (N) AS (SELECT Row_Number() OVER (ORDER BY (SELECT 1)) FROM B), 
Dates AS (
    SELECT 
     N, 
     DateAdd(Day, N, '18991231') Dte, 
     DateAdd(Day, N/7 * 7, '19000101') CalcDate 
    FROM C 
) 
INSERT dbo.BusinessDate 
SELECT 
    Dte, 
    Year(CalcDate), 
    DateDiff(Day, DateAdd(Year, DateDiff(Year, 0, CalcDate), 0), CalcDate)/7 + 1, 
    0, 
    (N + 6) % 7/5 -- calculate weekends 
FROM Dates; -- 3-7 seconds or so on my VM server 

然后加入到上的日期表,并使用WholeWeekNumber列的输出。你也可以考虑增加一个WeekNumberYear,因为如果没有这个,2009-01-01的52个确实属于2008年是很难的......一个奇怪的数据点在那里肯定如果你不知道(笑)。

示例表内容:

BusinessDate WholeWeekYear WholeWeekNumber Holiday Weekend BusinessDay 
------------ ------------- --------------- ------- ------- ----------- 
    1/1/2009   2008    52  0  0   1 
    1/2/2009   2008    52  0  0   1 
    1/3/2009   2008    52  0  1   0 
    1/4/2009   2009    1  0  1   0 
    1/5/2009   2009    1  0  0   1 
    1/6/2009   2009    1  0  0   1 
    1/7/2009   2009    1  0  0   1 
    1/8/2009   2009    1  0  0   1 
    1/9/2009   2009    1  0  0   1 
    1/10/2009   2009    1  0  1   0 
    1/11/2009   2009    2  0  1   0 

如果你真的不想使用此作为一般业务日期计算表,您可以删除最后3列,否则,更新假日列1公司假期。注意:如果您实际上制作了上表,并且您对其的访问最经常使用与BusinessDate不同的列上的JOIN或WHERE条件,则使主键非聚集并添加以备用列开始的聚集索引。

上述一些脚本需要SQL 2005或更高版本。

+0

我不喜欢第一个函数的想法,仅仅是因为你已经承认,当每行被调用时,性能不会很好(并且我不相信你的内联技巧会像你想象的那样执行)。我认同精神与日历表的想法,但在这种情况下,我认为在飞行中生成特定年份的日历已足够,并且似乎没有任何需要假期/周末/工作日等。另外,我认为你的一周如果我理解了要求权利,那么2009年1月1日的52个数字是不正确的 - 这些日期在2008年或2009年不会属于任何整周。 –

相关问题