【问题标题】:Auto pickup dates from SQL Server - T-SQL从 SQL Server 自动取货日期 - T-SQL
【发布时间】:2023-03-26 01:38:01
【问题描述】:

我正在寻找一些 T-SQL 代码,它应该选择“从当前日期回溯一年(1 月的最后一个星期日)”的日期。

例如:

Current day      expected result
2017-02-05       2016-01-31
2017-01-05       2015-01-25
2018-02-19       2017-01-29
2018-01-19       2016-01-31
2019-02-28       2018-01-28

请注意:年份从一月的最后一个星期日开始

我有一些 T-SQL 代码正在 SQL Server 2014 中使用:

select 
convert(varchar(10), DATEADD(day, DATEDIFF(day, '19000107', DATEADD(month, DATEDIFF(MONTH, 0, CONVERT(date, CONVERT(VARCHAR(4), (CASE WHEN MONTH(GetDate()) = 1 THEN CONVERT(VARCHAR(4), GetDate(), 112) - 1 ELSE CONVERT(VARCHAR(4), GetDate(), 112) END), 112) + '0101')), 30)) / 7  * 7, '19000107'), 120)

上面的代码选择了当年的日期(一月的最后一个星期日)。但我希望 T-SQL 代码选择去年(1 月的最后一个星期日的日期)的日期。

详细说明 - 我希望 T-SQL 代码从下表中生成 预期结果

Current day      T-SQL code answer       expected result
2017-02-05       2017-01-29              2016-01-31
2017-01-05       2016-01-31              2015-01-25
2018-02-19       2018-01-28              2017-01-29
2018-01-19       2017-01-29              2016-01-31
2019-02-28       2019-01-27              2018-01-28

请帮忙。

【问题讨论】:

  • 将日期解析和操作为字符串通常是不受欢迎的,我猜你没有写那个表达式。如果您想要一年前的日期,只需将每个 getdate() 替换为 dateadd(year, -1, getdate()

标签: sql sql-server sql-server-2008 tsql date


【解决方案1】:

这个问题最好的办法是数字和日期表。 This answer shows you how to create one。这样的表在很多情况下都很帅……

如果我理解正确,您希望在所有情况下都是上一年 1 月的最后一个星期日?试试这个:

DECLARE @dummy TABLE(ID INT IDENTITY,YourDate DATE);
INSERT INTO @dummy VALUES
('2017-02-05'),('2017-01-05'),('2018-02-19'),('2018-01-19'),('2019-02-28');

WITH Years AS
(
    SELECT * FROM (VALUES(2010),(2011),(2012),(2013),(2014),(2015),(2016),(2017),(2018),(2019),(2020)) AS t(Yr)
)
,LastSundays AS
(
    SELECT Yr AS TheYear
          ,DATEADD(DAY,(DATEPART(WEEKDAY,LastOfJanuary) % 7)*(-1),LastOfJanuary) AS LastSundayOfJanuary
    FROM Years
    CROSS APPLY(SELECT CAST(CAST(Yr AS VARCHAR(4)) + '0131' AS DATE)) AS t(LastOfJanuary)
)
SELECT * 
FROM @dummy AS d
INNER JOIN LastSundays AS ls ON YEAR(DATEADD(YEAR,-1,d.YourDate))=ls.TheYear;

结果(我完全看不懂第 2 行和第 4 行...

ID  YourDate    TheYear LastSundayOfJanuary
1   2017-02-05  2016    2016-01-31
2   2017-01-05  2016    2016-01-31 <--Your sample data is different...
3   2018-02-19  2017    2017-01-29
4   2018-01-19  2017    2017-01-29 <--Your sample data is different...
5   2019-02-28  2018    2018-01-28

提示您可能需要在计算中引入@@DATEFIRST...

【讨论】:

  • 我认为您的困惑是基于 OP 将“年”定义为 开始 在一月的最后一个星期日。因此,2017-01-05 是 2016 年的 OP 年,而不是 2017 年。同样,2018-01-19 是 2017 年的 OP 年,而不是 2018 年。在这两种情况下,结果年都需要回退一年。剩余的日期是在一月的第一个星期日之后,并且公历和 OP 年份恰好匹配。
【解决方案2】:

这是一种无需日期表的方法(顺便说一句,这仍然是一个好主意)。对您的所有输入进行了测试,每次都能提供正确的输出。显然,您会对其进行一些重构,因为它很冗长,只是为了显示每个步骤。

/* The input date. */

DECLARE
   @input DATE = '2019-02-28';

/* The input date less one year. */

DECLARE
   @date_minus_one_year DATE = DATEADD(yy,-1,@input);

/* The year part of the input date less one year. */

DECLARE
   @year_date_part INT = DATEPART(yy,@date_minus_one_year);

/* 31 Jan of the previous year. */

DECLARE
   @prev_year_jan_eom DATE = CAST(CAST(@year_date_part AS VARCHAR(4))+'-01-31' AS DATE);

/* What day of the week is 31 Jan of the previous year? */

DECLARE
   @previous_eom_dw_part INT = DATEPART(dw,@prev_year_jan_eom);

/* Offest 31 Jan to the previous Sunday, won't change if the 31st is itself a Sunday. */

DECLARE
   @output DATE = DATEADD(dd,1 - @previous_eom_dw_part,@prev_year_jan_eom);

/* Input and output */

SELECT
   @input input
  ,@output [output];

【讨论】:

    【解决方案3】:

    我没有想到没有case 中的条件的方法。它还使用了将数字年份值转换为 1 月 1 日日期的技巧。

    select case
      when
        datepart(dayofyear, dt) >
            31 - datepart(weekday, dateadd(day, 30, cast(year(dt) as varchar(4))))
      then
        dateadd(day,
            31 - datepart(weekday, dateadd(day, 30, cast(year(dt) as varchar(4)))),
            cast(year(dt) as varchar(4))
        )
      else
        dateadd(day,
            31 - datepart(weekday, dateadd(day, 30, cast(year(dt) - 1 as varchar(4)))),
            cast(year(dt) - 1 as varchar(4))
        )
    end
    from (values
        ('20100201'), ('20110301'), ('20120401'),
        ('20130501'), ('20140601'), ('20150701'),
        ('20160801'), ('20170901'), ('20181001')
    ) t(dt)
    

    只是为了好玩(未经测试)

    select
        dateadd(week,
            -52 * ceil(sign(datediff(day, dt, hs)) + 0.5),
            js
        )
    from
        (select <date> dt) as t
        cross apply
        (
        select 31 - datepart(weekday,
             datefromparts(year(dt), 1, 31) as js
         ) t2;
    

    【讨论】:

      【解决方案4】:
      SELECT
        convert(varchar(10), DATEADD(day, DATEDIFF(day, '19000107', DATEADD(month, DATEDIFF(MONTH, 0, CONVERT(date, CONVERT(VARCHAR(4), (CASE WHEN MONTH(DATEADD(year,-1,GetDate())) = 1 THEN CONVERT(VARCHAR(4), DATEADD(year,-1,GetDate()), 112) - 1 ELSE CONVERT(VARCHAR(4), DATEADD(year,-1,GetDate()), 112) END), 112) + '0101')), 30)) / 7  * 7, '19000107'), 120)
      

      【讨论】:

        猜你喜欢
        • 2017-07-11
        • 2017-07-11
        • 2017-07-12
        • 2015-05-19
        • 1970-01-01
        • 2020-09-21
        • 1970-01-01
        • 2017-05-17
        • 1970-01-01
        相关资源
        最近更新 更多