2

我必须计算在一个月的前“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。

4

9 回答 9

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
于 2009-05-11T17:30:45.940 回答
3

像这样的东西可能会起作用:

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 上的区域设置。一探究竟!

马克

于 2009-05-11T17:29:34.770 回答
2

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

于 2009-05-12T10:17:42.127 回答
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
    )
于 2009-05-11T17:29:49.857 回答
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。最后,我使用字符串连接计算过去一个月的第一天的方式是一种捷径。有更好的方法可以找到该月的实际第一天。

于 2009-05-11T17:31:09.230 回答
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
    )
于 2009-05-12T07:49:42.837 回答
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)
)
于 2013-08-06T08:26:47.183 回答
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,因此您只需两张支票即可获得第一个工作日

于 2015-03-24T14:55:16.240 回答
0
SELECT DATEADD(day, DATEDIFF (day, 0, DATEADD (month, DATEDIFF (month, 0, GETDATE()), 0)  -1)/7*7 + 7, 0);
于 2014-11-24T15:47:30.833 回答