【问题标题】:tsql day of weektsql 星期几
【发布时间】:2011-07-19 00:27:08
【问题描述】:

有人要求我进行 T-SQL 挑战,我需要根据这样的日期位置获得下个月的等效日期:

今天是 18/07/2011,也就是本月的第三个星期一...

现在我需要在 SQL Server 查询中获取下个月的第三个星期一(2011 年 8 月 15 日)。

这就像谷歌日历的重复规则。

我一直在尝试很多公式,但它变得非常复杂,非常感谢任何帮助。

【问题讨论】:

  • 这不只是:1)找出今天是哪一天(每月的第三个星期一)2)找到下个月相同类型的第一天(即第一个星期一)3 ) 如果我们想要 2nd 或更高,请将 date 增加 7 的适当倍数?

标签: sql-server tsql date


【解决方案1】:

这个语法会计算下周的同一天,如果不存在则输入null

declare @t datetime

set @t = '2008-01-29'

select case (datepart(day, @t)+ 6) / 7
           when (datepart(day, @t + 28)+ 6) / 7 then @t + 28
           when (datepart(day, @t + 35)+ 6) / 7 then @t + 35
           else null end

【讨论】:

  • +1 表示在无限期的情况下返回 NULL。
  • Andry - 感谢您的注意,我在给分后发布了它。所以我根本没想到我会得到任何功劳。
【解决方案2】:
CREATE FUNCTION fnSameDayOfWeekNextMonth (@Date datetime)
RETURNS datetime
AS BEGIN
  RETURN (
    SELECT
      DATEADD(WEEK,
              CASE (MONTH(ApproxDate) - MONTH(@Date) + 12) % 12
                WHEN 2 THEN -1
                ELSE ThisDayNum - (DAY(ApproxDate) - 1) / 7
              END,
              ApproxDate)
    FROM (
      SELECT
        ThisDayNum = (DAY(@Date) - 1) / 7,
        ApproxDate = DATEADD(WEEK, 5, @Date)
    ) s
  )
END

该函数实现如下逻辑:

  1. 获取给定日期加上正好 5 周的大致日期。

  2. 获取给定日期所在月份的星期几。

  3. 如果估计日期的月份比给定日期的月份晚 2 个月,则从估计日期减去一周并返回结果。

  4. 否则得到与 #2 相同的结果,但为近似日期(而不是给定日期)。

  5. 获取#2 和#4 之间的区别。

  6. 从估计的日期中减去 #5 作为周数并返回结果。

第 3 步意味着如果当前日期是五分之一,那么生成的日期将是下个月的第四个相同的工作日,因为第五个是不可能的。 更新: 在我看来,@t-clausen.dk 提供了在这种情况下返回 NULL 的更好主意。上面的函数可以很容易地修改为遵循相同的约定:…WHEN 2 THEN -1… 部分应该简单地更改为…WHEN 2 THEN NULL…

【讨论】:

  • 嗨,有什么想法可以跳几个月吗?例如。从现在起 4 个月后的第三个星期一
  • @Milox:是的,我有一个非常模糊的想法,我目前正在尝试将其基于上述解决方案。到目前为止,这个问题被证明与你原来的问题有很大不同,尽管两者非常相关。如果您真的有兴趣解决它,我建议您将您的请求作为一个新问题发布。有人可能会比我快。
  • 谢谢@Andriy M,我已经发布了我开发的解决该场景的功能,虽然不是很清楚,但它确实有效。请看下面
【解决方案3】:

幸运的是,我必须这样做一次:http://blog.prokrams.com/2007/06/18/determining-the-ordinal-of-a-weekday/

Create Function fn_ReturnOrdinalDay( @TestDate datetime) returns int
begin

 declare@ordinal int,@loopdate datetime

set @loopdate = @TestDateset 
set @ordinal = 0

while datepart(m, @TestDate) = datepart(m, @loopdate)
  begin
            set @ordinal = @ordinal + 1
            set @testdate = dateadd(d, -7, @testdate)
  end

return @ordinal
end

ETA:要查找同一序数的下一天,您只需在今天的日期上不断添加 +7,直到 fn_ReturnOrdinalDay 返回与今天相同的结果,当然。

【讨论】:

  • 嗨,我做了一些数学运算,结果是这样的: 天花板( cast(datepart(day,getdate())as float)/7 ) 这将从您的日期开始倒数以获得序数除法并四舍五入
【解决方案4】:

我会使用日历表来执行此操作:您可以每天填充一行,然后您只需要编写一个简单的查询来回答您的问题。您仍然可以使用其他发帖人建议的功能和逻辑来填充表格,但这将是一次性任务。如果您对重复日期做了大量工作,您可以有一个包含字符串值“第一个星期一”、“第三个星期三”等的列,以便于查询。

您的查询可能如下所示:

select 
    min(BaseDate)
from 
    dbo.Calendar
where 
    DayDescription = (select DayDescription from dbo.Calendar where BaseDate = @Today) and
    BaseDate > @Today

换句话说,给我今天之后的第一个日期,其中 DayDescription(即“第三个星期一”或其他)与今天相同。该日期必须是下个月的第三个星期一。确切的查询取决于您的数据类型和搜索参数值是什么。

作为一般观察,许多(大多数?)与日期相关的查询使用预填充的表格比使用函数更容易回答。

【讨论】:

  • 谢谢你,我敢打赌这会更快,但它对我不起作用,因为我可以将日期从今天到十年或更长时间
  • @Milox - 为什么它不起作用?您可以根据需要提前填充尽可能多的数据。我有一个包含 100 年数据的日​​历表,只有 36500 行(加上闰日)。填充 10 年、100 年还是 1000 年并不重要,因为这是一次性任务,与数据库的其余部分相比,数据存储微不足道。
  • 抱歉@Pondlife 耽搁了,我一直在处理日期,这似乎是一个很好的方法,你对性能很了解,它会起作用
【解决方案5】:

如果您只需要完成上述挑战,那么这相对容易。

  1. 使用日期格式函数找出日期的名称(我不记得确切的名称)。

  2. 从月初开始循环并记录数量,例如在您到达输入日期之前遇到的星期二。

  3. 然后在下个月(或任何一个月)循环 x 次,一旦达到所需的次数,则在下周二进行

【讨论】:

    【解决方案6】:

    -- 第三个星期三

    DATEPART(dw, PromptDate) + DATEPART(dw, PromptDate - 7) + DATEPART(dw, PromptDate - 14) = 12

    -- 不应该是第四个或第五个星期三,因为我们只对第三个星期三感兴趣。

    AND DATEDIFF(MONTH, PromptDate - 14, PromptDate - 21) 0

    【讨论】:

      【解决方案7】:

      这可能对某人有所帮助,如果您想从现在开始获得任意几个月的序数日期,我已经创建了这个函数作为您所有建议的组合:

       CREATE FUNCTION [dbo].[fncSameDayOfWeekAnotherMonth] (@Date datetime, @Months int)
      RETURNS datetime
      AS BEGIN
        declare @FONM datetime,  @day TinyInt, @ordinal tinyint, @FDM datetime, @result datetime
      
        Set @FONM = DATEADD(dd,-(DAY(DATEADD(mm,@Months,@Date))-1),DATEADD(mm,@Months,@Date))--first day of month
        Set @day = datepart(weekday,@Date)+7
        set @ordinal=ceiling( cast(datepart(day,@Date)as float)/7 )--ordinal day
        set @FDM = DateAdd(day, ((@day -DatePart(weekday, @FONM))%7), @FONM)--first day of week (monday, friday, etc) on month
      
        set @result = DateAdd(day, (@ordinal-1)*7, @FDM)
      
          RETURN case when DATEDIFF(month, @Date, @result) >  @Months  then DateAdd(day, (@ordinal-2)*7, @FDM) else @result end --if result exceeds number of months just subtract one week from ordinal
      
      END
      

      它接收日期和月数,获取您想要的日期和月份的第一天的序数。最后一个找到该月中第一次出现的工作日,然后计算从您的日期开始的任何月份的相应序数日期。

      我知道它有很多代码,可能会有所改进,但至少它工作得很好。

      【讨论】:

      • 如果 @Date 是该月某个工作日的第 5 次出现,并且计算月份的相应日期未定义,则您的函数返回同一工作日的第 4 次出现,而不是 NULL,但我知道这是您对此类情况的特别偏好。否则,此功能似乎运行良好,只是它在年份转换时失败。我认为这可以通过使用DATEDIFF(mm, …) > @Month 而不是MONTH(…) > MONTH(…) + @Month 来解决。
      • 哪个日期没有用?,我已经测试了几个案例,它可以持续到明年
      • SELECT dbo.fncSameDayOfWeekAnotherMonth('20111129', 3)。我应该说,它在某些情况下在年度过渡时失败了。 “某些情况”是指日期是第 5 个工作日,而您指定的月份将比原来的数字少,并且没有第 5 个工作日。这就是为什么 DATEDIFF 会更可靠。
      • 你是对的,即使使用 datediff 我得到一个错误的日期,我必须弄清楚
      • 我使用的 datediff 参数向后,现在已修复和编辑,谢谢@Andriy M
      猜你喜欢
      • 2013-12-31
      • 2016-10-22
      • 2015-05-13
      • 2022-08-10
      • 1970-01-01
      • 1970-01-01
      • 2022-07-08
      • 1970-01-01
      • 2021-06-29
      相关资源
      最近更新 更多