簡體   English   中英

在SQL中計算給定月份的第一個工作日

[英]Calculating in SQL the first working day of a given month

我必須計算在一個月的前N天支付的所有發票。 我有兩張桌子

發票:它有發票信息。 唯一重要的字段稱為“datePayment”

HOLYDAYS:這是一個列表。 此表的條目格式為“2009-01-01”,2009-05-01“等等。

我也應該考慮星期六和星期日(這可能不是問題,因為我可以在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。

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

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

像這樣的東西可能會起作用:

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上的語言環境。 看看這個!

我們使用日歷表方法而不是要排除的假日表,我們使用日歷表方法:應用程序每天需要一行(三十年跨越一個適度的11K行)。 因此,它不僅具有is_weekday列,還具有與企業相關的其他內容,例如julianized_date 這樣,每個可能的日期都會為first_working_day_this_month准備好一個值,並且發現它涉及一個簡單的查找(SQL產品傾向於優化!),而不是每次動態“計算”它。

返回當前月份的第一個星期一

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

我們的應用程序中有日期表(填充所有日期和日期部分已有數十年),允許各種“缺失”日期操作,如(在偽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
    )

好的,在第一次嘗試時,您可以將以下代碼放入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。 最后,我使用字符串連接計算過去一個月中的第一個的方式是一個捷徑。 有更好的方法可以找到本月的實際第一天。

獲取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
    )
SELECT DATEADD(day, DATEDIFF (day, 0, DATEADD (month, DATEDIFF (month, 0, GETDATE()), 0)  -1)/7*7 + 7, 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,因此您只需要兩張支票即可獲得第一個工作日

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM