简体   繁体   English

自动提取日期来自SQL Server - T-SQL

[英]Auto pickup dates from SQL Server - T-SQL

I am looking for some T-SQL code that should pick the date which is "One Year back from current date (at the same time last Sunday in the month of January)". 我正在寻找一些T-SQL代码,它应该选择“从当前日期开始的一年(在1月份的同一个星期日的同一时间)”。

For example: 例如:

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

Please note: The year starts from last Sunday in January 请注意:年份从1月的最后一个星期日开始

I have some T-SQL code which is being used in SQL Server 2014: 我有一些在SQL Server 2014中使用的T-SQL代码:

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)

The above code picks the date for current year's (last Sunday in January month). 上面的代码选择当前年份的日期(1月份的最后一个星期日)。 But I want T-SQL code to pick last year's (last Sunday's date in January month) date. 但我希望T-SQL代码选择去年(上个星期日的1月份日期)日期。

In detail - I want T-SQL code to produce expected result from below table 详细说 - 我希望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

Any help please. 请帮忙。

The best thing for this question is a numbers and date table. 这个问题的最好的事情是数字和日期表。 This answer shows you how to create one . 这个答案向您展示了如何创建一个 Such a table is very handsome in many situations... 在很多情况下,这样的桌子很帅......

If I understand this correctly, you want the last Sunday in January of the previous year in all cases? 如果我理解正确的话,你想要在所有情况下在去年1月的最后一个星期天? Try this: 尝试这个:

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;

The result ( I do not understand row 2 and 4 completely... ) 结果( 我完全不理解第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

Hint You might need to introduce @@DATEFIRST into your calculations... 提示您可能需要在计算中引入@@DATEFIRST ...

Here is a way to do it without a date table (which is still a good idea BTW). 这是一种没有日期表的方法(这仍然是一个好主意BTW)。 Tested on all your inputs and it delivers the correct output each time. 测试所有输入,每次都输出正确的输出。 Obviously you would refactor this a bit as it's longwinded, just to show each step. 显然你会稍微重构一下,因为它已经很长,只是为了显示每一步。

/* 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];

I didn't think of a way to do it without the conditional in a case . 在没有条件的case下,我没有想到这样做的方法。 It also uses the trick of casting a numeric year value to a January 1st date. 它还使用将数字年份值转换为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)

Just for fun (untested) 只为了好玩(未经测试)

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;
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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM