在SQL中计算给定月份的第一个工作日

时间:2009-05-11 17:11:21

标签: sql date-arithmetic

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

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

。 HOLYDAYS:这是一个列表。此表的参赛作品格式为“2009-01-01”,   2009-05-01“等等。

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

问题在于计算哪个是“付款限额”。

select count(*) from invoice 
where datePayment  < PAYMENTLIMIT

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

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

任何提示?

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

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

然后可以减少问题以计算每个月的FIRST_WORKING_DAY。

9 个答案:

答案 0 :(得分:4)

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

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

答案 1 :(得分:2)

这样的事可能有用:

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上的语言环境。看看吧!

马克

答案 2 :(得分:2)

我们使用日历表方法而不是要排除的假日表,我们使用日历表方法:应用程序每天需要一行(三十年跨越适度的11K行)。因此,它不仅具有is_weekday列,还具有与企业相关的其他内容,例如julianized_date。这样,每个可能的日期都会为first_working_day_this_month准备好一个值,并且发现它涉及一个简单的查找(SQL产品倾向于优化!),而不是每次动态“计算”它。 / p>

答案 3 :(得分:1)

我们的应用程序中有日期表(填充所有日期和日期部分已有数十年),什么允许各种“缺失”日期操作,如(在伪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
    )

答案 4 :(得分:1)

好的,在第一次尝试时,您可以将以下代码放入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。最后,我使用字符串连接计算过去一个月中的第一个的方式是一个捷径。有更好的方法可以找到当月的第一天。

答案 5 :(得分:1)

获取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 :(得分:1)

返回当月的第一个星期一

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

答案 7 :(得分:0)

SELECT DATEADD(day, DATEDIFF (day, 0, DATEADD (month, DATEDIFF (month, 0, GETDATE()), 0)  -1)/7*7 + 7, 0);

答案 8 :(得分:0)

select if(weekday('yyyy-mm-01') < 5,'yyyy-mm-01',if(weekday('yyyy-mm-02') < 5,'yyyy-mm-02','yyyy-mm-03'))

星期六和星期日是5,6,因此您只需要两张支票即可获得第一个工作日