1

现在的时间是:(m/d/yyyy) => 2009/01/04

在此处输入图像描述

使用此命令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 个整周)。

在此处输入图像描述

所以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
...
4

6 回答 6

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;
于 2012-07-03T20:49:35.343 回答
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-09-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 列,否则,将公司假期的 Holiday 列更新为 1。

注意:如果您确实制作了上面的表,并且您对它的访问通常在不同的列上使用 JOIN 或 WHERE 条件BusinessDate,则将主键设为非聚集的并添加从备用列开始的聚集索引。

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

于 2012-07-03T23:08:19.163 回答
2

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

然后你只需加入 date-to-date 并获得你想要的星期数。

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
于 2012-07-03T20:24:30.113 回答
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);
于 2012-07-03T20:38:05.940 回答
0

不要使用 UDF,而是使用日历表,然后您可以完全按照您的公司需要定义周数,并简单地从表中查询它们,这将比使用 UDF 更容易并且可能更快。

日历表在 SQL 中有很多用途(搜索此站点或 Google),所以无论如何您都应该创建它。

于 2012-07-03T20:26:08.147 回答
-1

对此没有好的答案。

一年不是 52 周。

平年是 52 周零一天,闰年是 52 周零两天。

于 2012-07-03T20:23:10.257 回答