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 つの日付は 1 週間の一部ではないため、固定の 52 週間のチャートに入れることはできません。

私たちの会社は、1 年間の52 週間の各週に関する情報を確認する必要があります。(毎年 52 週間あります)。

ここに画像の説明を入力

したがって20090101、2009 年の最初の週には属しません。

前年のものです(私の質問とは関係ありません

だから私はUDFが必要です(私はたくさん検索していて、ISOWEEK私のニーズに答えていませdatetimeWeek 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年)、その年の最後の1日か2日が53週目に入る可能性があるため、除外されることに注意してください。

別のアプローチは、MIN式を2回繰り返すことです。

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;

行ごとに1回呼び出されるため、パフォーマンスが低下する可能性があるため、使用することはお勧めしません。実際のクエリで使用する関数がどうしても必要な場合は、それを単一の列と行を返すインライン関数に変換し、そのまま使用します。

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.8 MB しかないので、サイズが大きくても心配する必要はありません)。

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 列を出力に使用します。これがないと、2009-01-01 の 52 が実際に 2008 年に属していることを理解するのが少し難しくなるので、WeekNumberYear を追加することも検討してください。笑う)。

テーブルの内容の例:

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

年の日付ごとに1行のカスタムカレンダーテーブルを設定し、他のフィールドを設定して、必要に応じてロールアップできるようにするのは比較的簡単です。これは、さまざまなカレンダー、つまり会計年度を使用しているクライアントがいる場合に行います。これにより、クエリロジックが非常に単純になります。

次に、日付から日付に参加して、必要な週番号を取得します。

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

これに対する良い答えはありません。

1年は52週間ではありません。

通常の年では52週と1日、うるう年では52週と2日です。

于 2012-07-03T20:23:10.257 に答える