【问题标题】:SQL - Check if a date is the first occurrence of that day in its monthSQL - 检查日期是否是该日期在其月份中的第一次出现
【发布时间】:2014-11-06 21:15:37
【问题描述】:

我想用 SQL 过滤一些日期。

我希望能够传递一个标志,表示保留从 X 日期到 Y 日期的月份的所有第一个星期一。

所以本质上,我想传入一个日期并能够判断它是给定月份的第一个、第二个、第三个、第四个还是最后一个星期一(例如)。

我已经过滤了月份和日期,我目前正在使用 DATEPART(DAY, thedate) 检查日期是否

所以我坚持的部分是Where IsDateFirstOfMonth(@date) 我从哪里开始编写函数IsDateFirstOfMonth? 非常感谢任何帮助

【问题讨论】:

  • 检查该月的某天是否小于 8 有什么问题?
  • 您是否试图确定 @date 是第一个星期一,还是在该月的第一周(即,是否与一年中的第一个日期在同一周)同月)?听起来您几乎要同时尝试两者。
  • 嗯,我有一个完整的日期列表。让我们说从今天到年底的所有星期一。我想要选择返回每个月的第一个或第二个或第三个或第四个或最后一个星期一。所以如果我使用 >8 或 >15 等的逻辑......它并不总是适用于第四个或最后一个
  • 如果第四个星期一也是最后一个星期一呢?
  • 您可以使用从下个月1号减去8天的技巧来确定是否是最后一天。

标签: sql sql-server tsql datetime


【解决方案1】:

对于这类问题,通常更容易实现具有所需日期信息的表并在该表上连接,并使用它进行过滤。 IE。使用此信息创建一个表:

CREATE TABLE Dates(
  Date DATE PRIMARY KEY CLUSTERED,
  PositionInMoth TINYINT,
  LastInMonth BIT)

然后使用您想要的任何方法填写此表。我认为您可以使用简单的临时应用程序轻松完成此操作,但您也可以使用 T-SQL 脚本创建它。

然后您只需将您的表与该表连接起来,并使用 PositionInMoth 或 LastInMonth 列进行过滤。您也可以将其用作查找表来轻松实现所需的功能。

顺便说一句,不要忘记有很多月份都有第五个特定日期,例如,2014 年 12 月有 5 个星期一、5 个星期二和 5 个星期三。给定 motnh 中有 5 个实例的天数是:一个月中的天数 - 28,例如,12 月是 31-28 = 3。所以你不能指望 4 号是最后一个。

这个表占用的空间真的很小,大概,DATE占3个字节,TINYINT占1个字节,BIT占1个字节,所以是3+1 +1 = 每天 5 字节,每年 1,825 字节,整个世纪为 178 kb。所以,即使你需要几个世纪来涵盖所有可能的日期,我仍然是一张很小的桌子。我说大致是因为索引结构、填充因子和其他一些东西会使表格更大一些。如此小的表意味着 SQL Server 在执行查询时可以轻松地将整个表缓存在内存中,因此它们会运行得非常快。

注意:您可以通过添加新的 BIT 列来扩展此表以涵盖其他需求,例如检查一天是一个月中的最后一天,还是一个月中的最后一个工作日或第一个工作日

非常有趣的链接,来自 OP 评论:CALENDAR TABLES IN T-SQL

【讨论】:

【解决方案2】:

你可以这样做

alter function IsDateFirstOfMonth(@date as datetime)
returns int
as
begin
    declare @first datetime,
            @last datetime,
            @temp datetime,
            @appearance int
    declare @table table(Id int identity(1,1),
                         StartDate datetime,
                         EndDate datetime,
                         DayName nvarchar(20),
                         RowNumber int)
    set @first=dateadd(day,1-day(@date),@date)
    set @last=dateadd(month,1,@first)-1
    set @temp=@first
    while @temp<=@last
    begin
        insert into @table(StartDate,EndDate,DayName) values(@temp,@temp+6,datename(dw,@temp))
        set @temp=@temp +1
    end     
    select @appearance=Nb
    from(
    select StartDate,EndDate,DayName,row_number() over (partition by DayName order by StartDate) as Nb 
    from @table) t
    where @date between t.StartDate and t.EndDate and datename(dw,@date)=t.DayName

    if @last-@date<7 
      set @appearance=-1

    return @appearance
end



select dbo.IsDateFirstOfMonth('31 Dec 2014')    
    select dbo.IsDateFirstOfMonth('03 Nov 2014') -- result 1 ( first) monday
    select dbo.IsDateFirstOfMonth('10 Nov 2014') -- result 2 (second)
    select dbo.IsDateFirstOfMonth('17 Nov 2014') -- result 3 (third)
    select dbo.IsDateFirstOfMonth('24 Nov 2014') -- result -1 (last) .... here it will be the last monday
    select dbo.IsDateFirstOfMonth('02 Nov 2014') -- result 1 ( first) sunday

希望对你有帮助

【讨论】:

  • 非常感谢您的帮助。这很好用,但似乎只适用于星期一?也许我不太清楚,但我只是以星期一为例。如果我删除星期一的 if 检查并使用“2014 年 11 月 15 日”运行它,我会得到 2 而不是 3,因为它是 11 月的第三个星期六。再次感谢您的帮助。另外,如果您不介意解释所有嵌套的日期差异如何工作,那将不胜感激,也许我可以自己调试;)
  • 去掉 if(datename(dw,@date)='Monday'),那么它将在一周内的所有日子里工作
  • 我做到了,但是就像我说的那样,当我使用“2014 年 11 月 15 日”运行它时,我得到 2 而不是预期的 3,因为它是 11 月的第三个星期六。干杯
  • 非常感谢哈迪,因为我知道这是一个棘手的问题。对于像 select dbo.IsDateFirstOfMonth('24 DEC 2014') 这样的日期,我似乎仍然有奇怪的行为,当它不是 12 月的最后一个星期三时给出 -1,并且 select dbo.IsDateFirstOfMonth('05 DEC 2014') 给出了 0。
  • @user2329438 我以不同的方式完全重写了该函数,这次该函数将占用整个月并将构建本月的星期,然后根据日期名称和在end 会检查日期是否是最后一个,希望这次能解决您的问题
【解决方案3】:

感谢您的所有帮助。 Hadi,您的回复效果很好,但我还是决定使用计数表

我在这里找到了一个很好的例子 http://blog.aware.co.th/calendar-tables-in-t-sql/

再次感谢

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多