![](/img/trans.png)
[英]SQL Server 2008 - Sum business minutes between two dates taking into account custom holidays and weekends
[英]difference between two dates without weekends and holidays Sql query ORACLE
我有2個表:第一個包含采購訂單的開始日期和結束日期,第二個表包含年份hollidays
-采購訂單
-Holidays
我試着計算兩個日期之間沒有周末和假期的工作日數。
輸出應該是這樣的:
Start Date | End Date | Business Days
請你幫助我好嗎
您可以使用以下查詢刪除非周末假期:
select (t.end_date - t.start_date) - count(c.date)
from table1 t left join
calendar c
on c.date between t1.start_date and t1.end_date and
to_char(c.date, 'D') not in ('1', '7')
group by t.end_date, t.start_date;
因此,刪除周末日更加復雜。 整周有兩個周末,所以這很容易。 所以一個很好的近似是:
select (t.end_date - t.start_date) - (count(c.date) +
2 * floor((t.end_date - t.start_date) / 7))
from table1 t left join
calendar c
on c.date between t1.start_date and t1.end_date and
to_char(c.date, 'D') not in ('1', '7')
group by t.end_date, t.start_date;
這不會得到星期幾,這主要是如果結束日期在開始日期之前,那么它是在下一周。 但是,這種邏輯在Oracle處理星期幾的方式上變得相當復雜,所以上面的近似值就足夠了。
編輯:我忽略了Oracle標記的存在,並跳轉到SQL Server腳本。 但這個概念並沒有改變。
為了超級准確,我將使用以下格式創建一個表格。
Year int,month int,DaysInMonth int,firstOccuranceOfSunday int
創建一個過程以從該表上的特定年份和月份中提取周末。
CREATE FUNCTION [dbo].[GetWeekendsForMonthYear]
(
@year int,
@month int
)
RETURNS @weekends TABLE
(
[Weekend] date
)
AS
BEGIN
declare @firstsunday int = 0
Declare @DaysInMonth int = 0
Select @DaysInMonth = DaysInMonth, @firstsunday = FirstSunday from Months
Where [Year] = @year and [month] = @month
Declare @FirstSaterday int = @firstsunday - 1
declare @CurrentDay int = 0
Declare @CurrentDayIsSunday bit = 0
if @FirstSaterday !< 1
Begin
insert into @Weekends values(DATEADD(year, @year -1900, DATEADD(month, @month -1, DATEADD(day, @Firstsaterday -1, 0))))
insert into @Weekends values(DATEADD(year, @year -1900, DATEADD(month, @month -1, DATEADD(day, @FirstSunday -1, 0))))
set @CurrentDayIsSunday = 1
set @CurrentDay = @firstsunday
END
else
begin
insert into @Weekends values(DATEADD(year, @year -1900, DATEADD(month, @month -1, DATEADD(day, @FirstSunday -1, 0))))
set @FirstSaterday = @firstsunday + 6
insert into @Weekends values(DATEADD(year, @year -1900, DATEADD(month, @month -1, DATEADD(day, @Firstsaterday -1, 0))))
set @CurrentDayIsSunday = 0
set @CurrentDay = @FirstSaterday
end
declare @done bit = 0
while @done = 0
Begin
if @CurrentDay <= @DaysInMonth
Begin
If @CurrentDayIsSunday = 1
begin
set @CurrentDay = @CurrentDay + 6
set @CurrentDayIsSunday = 0
if @CurrentDay <= @DaysInMonth
begin
insert into @Weekends Values(DATEADD(year, @year -1900, DATEADD(month, @month -1, DATEADD(day, @CurrentDay -1, 0))))
end
end
else
begin
set @CurrentDay = @CurrentDay + 1
set @CurrentDayIsSunday = 1
if @CurrentDay <= @DaysInMonth
begin
insert into @Weekends Values(DATEADD(year, @year -1900, DATEADD(month, @month -1, DATEADD(day, @CurrentDay -1, 0))))
end
end
end
ELSE
begin
Set @done = 1
end
end
RETURN
END
當調用並提供年份和月份時,這將返回表示周末的日期列表。
現在,使用該函數,創建一個過程,為特定日期范圍內的每個適用行調用此函數一次,並返回temptable中的值。
請注意,我現在發布這個,以便您可以看到正在發生的事情但我仍在繼續處理代碼。 我會發布更新。
更多信息:獲取特定日期范圍的周末列表(格式化),從該列表中刪除可在假期表中找到的任何日期。
不幸的是,我明天必須上班,上床睡覺。
此查詢應為purchase
表中的每個范圍生成確切的工作日數:
with days as (
select rn, sd + level - 1 dt, sd, ed
from (select row_number() over (order by start_date) rn,
start_date sd, end_date ed from purchase_order)
connect by prior rn = rn and sd + level - 1 <= ed
and prior dbms_random.value is not null)
select sd start_date, ed end_date, count(1) business_days
from days d left join holidays h on holiday_date = d.dt
where dt - trunc(dt, 'iw') not in (5, 6) and h.holiday_date is null
group by rn, sd, ed
對於purchase_orders
每一行,查詢都會從此范圍生成日期(這由子查詢dates
完成)。 主查詢檢查這是周末日或假日日,並計算其余日期。
如果purchase_orders
有大量數據或句點很長,用於生成日期的分層查詢可能會導致速度減慢。 在這種情況下,首選方法是創建日歷表,如評論中已建議的那樣。
由於您已經有一個假期表,您可以計算起始日期和結束日期之間的假期,並從結束日期和開始日期之間的天數之差中減去該假期。 對於周末,您需要一個包含類似於假期表的周末日的表格,或者您可以按照以下方式生成它們。
with sample_data(id, start_date, end_date) as (
select 1, date '2015-03-06', date '2015-03-7' from dual union all
select 2, date '2015-03-07', date '2015-03-8' from dual union all
select 3, date '2015-03-08', date '2015-03-9' from dual union all
select 4, date '2015-02-07', date '2015-06-26' from dual union all
select 5, date '2015-04-17', date '2015-08-16' from dual
)
, holidays(holiday) as (
select date '2015-01-01' from dual union all -- New Years
select date '2015-01-19' from dual union all -- MLK Day
select date '2015-02-16' from dual union all -- Presidents Day
select date '2015-05-25' from dual union all -- Memorial Day
select date '2015-04-03' from dual union all -- Independence Day (Observed)
select date '2015-09-07' from dual union all -- Labor Day
select date '2015-11-11' from dual union all -- Veterans Day
select date '2015-11-26' from dual union all -- Thanks Giving
select date '2015-11-27' from dual union all -- Black Friday
select date '2015-12-25' from dual -- Christmas
)
-- If your calendar table doesn't already hold weekends you can generate
-- the weekends with these next two subfactored queries (common table Expressions)
, firstweekend(weekend, end_date) as (
select next_day(min(start_date),'saturday'), max(end_date) from sample_data
union all
select next_day(min(start_date),'sunday'), max(end_date) from sample_data
)
, weekends(weekend, last_end_date) as (
select weekend, end_date from firstweekend
union all
select weekend + 7, last_end_date from weekends where weekend+7 <= last_end_date
)
-- if not already in the same table combine distinct weekend an holiday days
-- to prevent double counting (in case a holiday is also a weekend).
, days_off(day_off) as (
select weekend from weekends
union
select holiday from holidays
)
select id
, start_date
, end_date
, end_date - start_date + 1
- (select count(*) from days_off where day_off between start_date and end_date) business_days
from sample_data;
ID START_DATE END_DATE BUSINESS_DAYS
---------- ----------- ----------- -------------
1 06-MAR-2015 07-MAR-2015 1
2 07-MAR-2015 08-MAR-2015 0
3 08-MAR-2015 09-MAR-2015 1
4 07-FEB-2015 26-JUN-2015 98
5 17-APR-2015 16-AUG-2015 85
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.