簡體   English   中英

沒有周末和假期的兩個日期之間的差異Sql查詢ORACLE

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

SQLFiddle演示

對於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.

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