我必须计算在一个月的前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。
答案 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,因此您只需要两张支票即可获得第一个工作日