简体   繁体   English

没有周末和假期的两个日期之间的差异Sql查询ORACLE

[英]difference between two dates without weekends and holidays Sql query ORACLE

I have 2 tables: the 1st one contains the start date and the end date of a purchase order, and the 2nd table contains year hollidays 我有2个表:第一个包含采购订单的开始日期和结束日期,第二个表包含年份hollidays

-purchase order -采购订单

在此输入图像描述

-Holidays -Holidays

I'm tryign to calculate the number of business days between 2 dates without the weekends and the holidays. 我试着计算两个日期之间没有周末和假期的工作日数。

the output should be like this: 输出应该是这样的:

Start Date | End Date | Business Days

Could you please help me 请你帮助我好吗

You can remove the non-weekend holidays with a query like this: 您可以使用以下查询删除非周末假期:

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;

Removing the weekend days is then more complication. 因此,删除周末日更加复杂。 Full weeks have two weekend days, so that is easy. 整周有两个周末,所以这很容易。 So a good approximation is: 所以一个很好的近似是:

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;

This doesn't get the day of week, which is essentially if the end date is before the start date, then it is in the following week. 这不会得到星期几,这主要是如果结束日期在开始日期之前,那么它是在下一周。 However, this logic gets rather complicated the way that Oracle handles day of the week, so perhaps the above approximation is sufficient. 但是,这种逻辑在Oracle处理星期几的方式上变得相当复杂,所以上面的近似值就足够了。

EDIT: I ignored the presence of the Oracle tag and jumped into scripting this for SQL Server. 编辑:我忽略了Oracle标记的存在,并跳转到SQL Server脚本。 The concept doesn't change though. 但这个概念并没有改变。

To be super accurate, I would create a table whit the following format. 为了超级准确,我将使用以下格式创建一个表格。

Year int, month int, DaysInMonth int, firstOccuranceOfSunday int Year int,month int,DaysInMonth int,firstOccuranceOfSunday int

您必须从日历中手动输入这些值

Create a Procedure to extract the weekends from a specific year and month on that table. 创建一个过程以从该表上的特定年份和月份中提取周末。

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

When called and provided with a year and month this will return a list of dates that represent weekends. 当调用并提供年份和月份时,这将返回表示周末的日期列表。

Now, using that function, create a procedure to call this function once for every applicable row in a specific date rang and return the values in a temptable. 现在,使用该函数,创建一个过程,为特定日期范围内的每个适用行调用此函数一次,并返回temptable中的值。

Note, I'm posting this now so you can see what's going on but I am continuing to work on the code. 请注意,我现在发布这个,以便您可以看到正在发生的事情但我仍在继续处理代码。 I will post updates as they arise. 我会发布更新。

More to come: Get list of weekends(formatted) for a specific daterange, remove any dates from that list which can be found on your holidays table. 更多信息:获取特定日期范围的周末列表(格式化),从该列表中删除可在假期表中找到的任何日期。

Unfortunately, I have to work tomorrow and am off to bed. 不幸的是,我明天必须上班,上床睡觉。

This query should produce exact number of business days for each range in purchase table: 此查询应为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 demo SQLFiddle演示

For each row in purchase_orders query generates dates from this range (this is done by subquery dates ). 对于purchase_orders每一行,查询都会从此范围生成日期(这由子查询dates完成)。 Main query checks if this is weekend day or holiday day and counts rest of dates. 主查询检查这是周末日或假日日,并计算其余日期。

Hierarchical query used to generate dates may cause slowdowns if there is big number of data in purchase_orders or periods are long. 如果purchase_orders有大量数据或句点很长,用于生成日期的分层查询可能会导致速度减慢。 In this case preferred way is to create calendar table, as already suggested in comments. 在这种情况下,首选方法是创建日历表,如评论中已建议的那样。

Since you already have a table of holidays you can count the holidays between the starting and ending date and subtract that from the difference in days between your ending and starting date. 由于您已经有一个假期表,您可以计算起始日期和结束日期之间的假期,并从结束日期和开始日期之间的天数之差中减去该假期。 For the weekends, you either need a table containing weekend days similar to your table of holidays, or you can generate them as below. 对于周末,您需要一个包含类似于假期表的周末日的表格,或者您可以按照以下方式生成它们。

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