簡體   English   中英

SQL:獲取基於當前年份周的第一天

[英]SQL: get first day of the current year week based

許多人建議如何獲取當前年份的第一個日期(基於月份),例如:

SELECT DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0)

這將導致:

2021-01-01 00:00:00.000

應該是 2021-01-04。 (第一周的星期一(美國的星期日))

我需要的是類似的但基於周的。 例如,第一周可能從 12 月 31 日開始,因此該函數應該返回 12 月 31 日,而不是 1 月 1 日。

有任何想法嗎 ?

為此,使用內聯表值函數比使用標量函數要好得多(這里有一些詳細信息)。

我根據上面評論中顯示的所需結果創建了一個為任何給定日期生成年份的第一個星期一。 如果您想要周日的東西,請在您的問題中提供示例數據/所需的結果:

CREATE FUNCTION dbo.GetFirstMonday
( 
  @date date
)
RETURNS TABLE
WITH SCHEMABINDING
AS
  RETURN 
  (
    WITH km(km) AS (SELECT CONVERT(date, '20180101')), -- known Monday
      d AS (SELECT FirstOfYear = DATEFROMPARTS(YEAR(@date),1,1)),
      w AS (SELECT FirstMonday = DATEADD(WEEK, DATEDIFF(WEEK, km, FirstOfYear) 
    + CASE WHEN ((@@DATEFIRST + DATEPART(WEEKDAY, FirstOfYear))) IN (6,13)
      THEN 1 ELSE 0 END, km) FROM d CROSS JOIN km)
    SELECT FirstMonday FROM w
  );

用法:

SET DATEFIRST 5;

DECLARE @src TABLE(InputDate date);

INSERT @src(InputDate) VALUES
('20160608'),('20170505'),('20180405'),
('20190303'),('20200903'),('20210706');

SELECT * FROM @src AS src 
  CROSS APPLY dbo.GetFirstMonday(src.InputDate);

結果(對於任何DATEFIRST設置):

InputDate   FirstMonday
----------  -----------
2016-06-08  2016-01-04
2017-05-05  2017-01-02
2018-04-05  2018-01-01
2019-03-03  2018-12-31
2020-09-03  2019-12-30
2021-07-06  2021-01-04

另一種思考方式是:如果一年的第一個星期一與一年的第一天相距超過2天(例如一年開始於 ,則取一年的第一天之前的第一個星期一,可以在最多 3 天前。因此,此函數從{first of year} - {3 days} => {first of year} + {3 days} - 其中一天的 7 天范圍中獲取匹配工作日的MIN()必須是您所追求的星期一(如果您的規則不是三天,則很容易改變):

CREATE FUNCTION dbo.GetFirstWeekday
(
  @date date,
  @DayName char(6)
)
RETURNS TABLE WITH SCHEMABINDING
AS
  RETURN (WITH n(n) AS (SELECT -3 UNION ALL SELECT n+1 FROM n WHERE n < 3),
    d(d) AS (SELECT DATEADD(DAY, n, DATEFROMPARTS(YEAR(@date),1,1)) FROM n)
    SELECT DayName = @DayName, FirstWeekday = MIN(d) FROM d 
      WHERE DATENAME(WEEKDAY, d) = @DayName
  );

所以給出:

DECLARE @src TABLE(InputDate date);

INSERT @src(InputDate) VALUES
('20160108'),('20170505'),('20180405'),
('20190303'),('20200403'),('20210506');

SELECT * FROM @src AS src 
  CROSS APPLY dbo.GetFirstWeekday(src.InputDate, 'Monday');

結果:

InputDate   FirstWeekday
----------  ------------
2016-06-08  2016-01-04
2017-05-05  2017-01-02
2018-04-05  2018-01-01
2019-03-03  2018-12-31
2020-09-03  2019-12-30
2021-07-06  2021-01-04

這也與@@DATEFIRST沒有任何關系,但它確實依賴於@@LANGUAGE在英語領域。

  • 示例db<>fiddle - 周日也在那里,但我在猜測您的規則,因為您沒有提供任何示例數據/所需的結果。

但實際上,如果幾年中的每一年都只有一個星期日或星期一,而您已經知道規則,為什么不創建一個表,定義一次這些規則,而不是提出古怪且明顯靈活的語法呢?

CREATE TABLE dbo.FirstSundayMondayRules
(
  TheYear int PRIMARY KEY,
  FirstSunday date NOT NULL,
  FirstMonday date NOT NULL
);

-- guessing at your Sunday rules here
INSERT dbo.FirstSundayMondayRules VALUES
(2016, '20160103', '20160104'),
(2019, '20181230', '20181231');

現在您可以創建一個更簡單的函數:

CREATE FUNCTION dbo.GetFirstWeekday
(
  @Date date,
  @DayName char(6)
)
RETURNS TABLE WITH SCHEMABINDING
AS
  RETURN (SELECT FirstWeekday = CASE @DayName 
      WHEN 'Sunday' THEN FirstSunday 
      WHEN 'Monday' THEN FirstMonday END
    FROM dbo.FirstSundayMondayRules
    WHERE TheYear = DATEPART(YEAR, @Date)
  );

並且DATEFIRST不再具有任何相關性,至少作為任何計算的一部分:

SET DATEFIRST 5;

DECLARE @src TABLE(InputDate date);

INSERT @src(InputDate) VALUES
('20160608'),('20190303');

SELECT 'Sunday', * FROM @src AS src 
  CROSS APPLY dbo.GetFirstWeekday(src.InputDate, 'Sunday');
  
SELECT 'Monday', * FROM @src AS src 
  CROSS APPLY dbo.GetFirstWeekday(src.InputDate, 'Monday');

結果:

(No column name)  InputDate   FirstWeekday
----------------  ----------  ------------
Sunday            2016-06-08  2016-01-03
Sunday            2019-03-03  2018-12-30

(No column name)  InputDate   FirstWeekday
----------------  ----------  ------------
Monday            2016-06-08  2016-01-04
Monday            2019-03-03  2018-12-31

或者更好的是,給自己一個Calendar 表,你可以只在列中硬編碼FirstSundayOfYearFirstMondayOfYear

所以我想出了一個解決方案,但可能有一個更優雅的解決方案


-- =============================================
-- Author:      PLANSIS
-- Create date: 2020-11-25
-- Description: first day of the current year week based (monday of the first week)
-- =============================================
  CREATE OR ALTER      FUNCTION [dbo].[A_FN_TI_FirstDayCurrentYearWeek] 
(
    @date date  = null
)
RETURNS  datetime
AS
BEGIN
    set @date = coalesce( @date , getdate() ) 
    
    DECLARE @ResultVar datetime 
    DECLARE @wk int  
    DECLARE @yr int  

    SET @yr = year(@date)
    SET @wk = 1

    SELECT @ResultVar  =  dateadd (week, @wk, dateadd (year, @yr-1900, 0)) - 4 -
         datepart(dw, dateadd (week, @wk, dateadd (year, @yr-1900, 0)) - 4) + 2 -- +2 for europe, +1 for US
    -- Return the result of the function
    RETURN @ResultVar 
END

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM