[英]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.