【问题标题】:Calculating in SQL the first working day of a given month在 SQL 中计算给定月份的第一个工作日
【发布时间】:2010-10-25 08:18:10
【问题描述】:

我必须计算在一个月的前“N”天支付的所有发票。我有两张桌子

。 INVOICE:有发票信息。唯一重要的字段称为“datePayment”

。 HOLYDAYS:这是一个单列表。此表中的条目格式为“2009-01-01”, 2009-05-01”等等。

我也应该考虑周六和周日 (这可能不是问题,因为我可以在 Hollidays 表中插入这些日子,以便在必要时将它们视为 hollidays)

问题是计算哪个是“支付限额”。

select count(*) from invoice 
where datePayment  < PAYMENTLIMIT

我的问题是如何计算此 PAYMENTLIMIT。其中 PAYMENTLIMIT 是“每月的第五个工作日”。

查询应在 Mysql 和 Oracle 下运行,因此应使用标准 SQL。

有什么提示吗?

编辑 为了与问题的标题保持一致,伪查询应如下所示:

select count(*) from invoice 
where datePayment  < FIRST_WORKING_DAY + N

那么问题可以简化为计算每个月的FIRST_WORKING_DAY。

【问题讨论】:

  • 第二张桌子的正确拼写是“holiday”
  • 其实这个表叫做'Feiertag'。 :-) 无论如何,感谢您的评论。会改正的。
  • 我无法理解您的问题。您想知道如何找到每个月的第一个工作日或付款限额是多少?付款限制是第一个工作日+5个工作日吗?

标签: sql date-arithmetic


【解决方案1】:
select if(weekday('yyyy-mm-01') < 5,'yyyy-mm-01',if(weekday('yyyy-mm-02') < 5,'yyyy-mm-02','yyyy-mm-03'))

星期六和星期日是 5、6,所以你只需要两张支票就可以得到第一个工作日

【讨论】:

    【解决方案2】:
    SELECT DATEADD(day, DATEDIFF (day, 0, DATEADD (month, DATEDIFF (month, 0, GETDATE()), 0)  -1)/7*7 + 7, 0);
    

    【讨论】:

      【解决方案3】:

      返回当月的第一个星期一

      SELECT DATEADD(
          WEEK,
          DATEDIFF( --x weeks between 1900-01-01 (Monday) and inner result
              WEEK,
              0, --1900-01-01
              DATEADD( --inner result
                  DAY,
                  6 - DATEPART(DAY, GETDATE()),
                  GETDATE()
              )
          ),
          0 --1900-01-01 (Monday)
      )
      

      【讨论】:

        【解决方案4】:

        我们使用日历表方法,而不是排除天数表,而是使用日历表方法:应用程序每天需要一行(30 年跨度适中的 11K 行)。所以它不仅有一个is_weekday 列,它还有其他与企业相关的东西,例如。 julianized_date。这样,每个可能的日期都会为first_working_day_this_month 准备好值,并且发现它涉及一个简单的查找(SQL 产品往往会针对它进行优化!),而不是每次都在运行中“计算”它。

        【讨论】:

          【解决方案5】:

          获取 2009 年每个月的前 N ​​个工作日:

          select * from invoices as x
          where 
              datePayment between '2009-01-01' and '2009-12-31'
          
          
              and exists
              ( 
                  select             
                           1
                  from invoices
                  where
                      -- exclude holidays and sunday saturday...
                      (
                          datepart(dw, datePayment) not in (1,7) -- day of week
          
          
                          /*
                          -- Postgresql and Oracle have programmer-friendly IN clause
                          and 
                          (datepart(yyyy,datePayment), datepart(mm,datePayment))
                          not in (select hyear, hday from holidays) 
                          */
          
          
                          -- this is the MSSQL equivalent of programmer-friendly IN
                          and 
                          not exists
                          (
                              select * from holidays
                              where 
                                  hyear = datepart(yyyy,datePayment)
                                  and hmonth = datepart(mm, datePayment)
                          )                                
                      )
                      -- ...exclude holidays and sunday saturday
          
          
          
                      -- get the month of x datePayment
                      and                 
                      (datepart(yyyy, datePayment) = datepart(yyyy, x.datePayment)
                       and datepart(mm, datePayment) = datepart(mm, x.datePayment)) 
          
          
                  group by 
                      datepart(yyyy, datePayment), datepart(mm, datePayment)    
          
                  having 
                      x.datePayment < MIN(datePayment) + @N -- up to N working days
              )
          

          【讨论】:

            【解决方案6】:

            您可以查找一个月中的第一个日期,该日期不在假期表中且日期不是周末:

            select min(datePayment), datepart(mm, datePayment)
            from invoices
            where datepart(dw, datePayment) not in (1,7) --day of week
            and not exists (select holiday from holidays where holiday = datePayment)
            group by datepart(mm, datePayment) --monthnr
            

            【讨论】:

            • 或者反过来...预先计算一个月中的所有第一个工作日并将它们存储在一个表中。所以你只需要查询这个表
            【解决方案7】:

            好的,在第一次尝试时,您可以将以下代码放入 UDF 中,并将年份和月份作为变量传递。然后它可以返回 TestDate,它是该月的第一个工作日。

            DECLARE @Month INT
            DECLARE @Year INT
            
            SELECT @Month = 5
            SELECT @Year = 2009
            
            DECLARE @FirstDate DATETIME
            SELECT @FirstDate = CONVERT(varchar(4), @Year) + '-' + CONVERT(varchar(2), @Month) + '-' + '01 00:00:00.000'
            
            DROP TABLE #HOLIDAYS
            CREATE TABLE #HOLIDAYS (HOLIDAY DateTime)
            
            INSERT INTO #HOLIDAYS VALUES('2009-01-01 00:00:00.000')
            INSERT INTO #HOLIDAYS VALUES('2009-05-01 00:00:00.000')
            
            DECLARE @DateFound BIT
            SELECT @DateFound = 0
            WHILE(@DateFound = 0)
            BEGIN
                IF(
                    DATEPART(dw, @FirstDate) = 1
                    OR
                    DATEPART(dw, @FirstDate) = 1
                    OR
                    EXISTS(SELECT * FROM #HOLIDAYS WHERE HOLIDAY = @FirstDate)
                )
                BEGIN
                    SET @FirstDate = DATEADD(dd, 1, @FirstDate)
                END
                ELSE
                BEGIN
                    SET @DateFound = 1
                END
            END
            
            SELECT @FirstDate
            

            我不喜欢这个解决方案的地方是,如果你的假期表包含一个月中的所有日子,就会出现无限循环。 (您可以检查循环是否仍在查看正确的月份)它依赖于相等的日期,例如所有时间都在 00:00:00。最后,我使用字符串连接计算过去一个月的第一天的方式是一种捷径。有很多更好的方法可以找到该月的实际第一天。

            【讨论】:

              【解决方案8】:

              我们的应用程序中有日期表(填充了数十年的所有日期和日期部分),允许各种“缺失”日期操作,例如(在伪 sql 中):

              select min(ourdates.datevalue)
              from ourdates
              where ourdates.year=<given year> and ourdates.month=<given month>
                  and ourdates.isworkday
                  and not exists (
                      select * from holidays
                      where holidays.datevalue=ourdates.datevalue
                  )
              

              【讨论】:

                【解决方案9】:

                这样的事情可能会奏效:

                create function dbo.GetFirstWorkdayOfMonth(@Year INT, @Month INT)
                returns DATETIME
                as begin
                    declare @firstOfMonth VARCHAR(20)
                    SET @firstOfMonth = CAST(@Year AS VARCHAR(4)) + '-' + CAST(@Month AS VARCHAR) + '-01'
                
                    declare @currDate DATETIME 
                    set @currDate = CAST(@firstOfMonth as DATETIME)
                
                    declare @weekday INT
                    set @weekday = DATEPART(weekday, @currdate)
                
                    -- 7 = saturday, 1 = sunday
                    while @weekday = 1 OR @weekday = 7
                    begin
                        set @currDate = DATEADD(DAY, 1, @currDate)
                        set @weekday = DATEPART(weekday, @currdate)
                    end
                
                    return @currdate
                end
                

                我不能 100% 确定“工作日”数字是固定的还是可能取决于 SQL Server 上的区域设置。看看吧!

                马克

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2021-12-18
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2013-04-03
                  • 2014-09-02
                  • 2020-02-27
                  相关资源
                  最近更新 更多