Beginner way to count days between dates excluding weekends and holidays - sql

I am a beginner in Postgres and looking for some help on a problem I am having with a query. I am trying to count the number of business days between two dates (exclude sat & sun & holidays). This is kind of what I am trying to do:
Select column1, column2, (current_date - defined_date) as elapsed_days
from mytable
where elapsed_days excludes sat, sun, and holidays

create table calendar c as
(cal_date date primary key,
business_day boolean not null);
insert into calendar
(select
('01/01/1900'::date + (g||' days')::interval)::date,
case extract(dow from '01/01/1900'::date
+ (g||' days')::interval)
when 0 then false when 6 then false else true end
from generate_series(0,365*150) g)
Now you have a calendar table populated with weekends set to "business_day=false" and all other days set to true.
You'll have to populate your other holidays manually or write a program to do that.
Afterwards, to calculate difference between days do something like:
select count(*) from cal
where cal between "start_date_var" and "end_date_var"
and business_day=true;
NOTE: If it were me, I'd add a few other columns to your calendar table so that it can include which holiday it is, or other things like that. May even have another table for holidays. This is a good start though.

Related

SQL calculate number of days in month excluding weekends and holidays days

I have approximately the same table (excluding count column). I want to calculate the number of working days (Mon-Fri) and exclude public holidays.
I tried to try the following query
SELECT count(distinct(date)) from MYDB where dummy <> 1
However, it gives the only total number of days including weekends. Additionally, if use this command it counts distinct dates, however, my dates do not show a full month, so another logic should've used. Could you help to figure out which code is better to use?
there should be a function in Vertica that extracts weekday from date, so to exclude weekends you'll need to add another condition like
extract(dow from date) not in (6,0)
(6 is Sat, 0 is Sun in this case)

Counting working days between two dates Oracle

I'm trying to write a query which can count the number of working days between a payment being received and being processed, I started playing around with this for payments received in December 2017;
select unique trunc(date_received),
(case when trunc(date_received) in ('25-DEC-17','26-DEC-17') Then 0 when
to_char(date_received,'D') <6 Then 1 else 0 end) Working_day
from payments
where date_received between '01-DEC-17' and '31-dec-17'
order by trunc(date_received)
But to be honest, I'm at a loss as to how to take it further and add in date_processed and count the number of working days between date_processed and date_received... Any help would be much appreciated...
Maybe not the most optimal, but it works quite nicely, and it's easy to incorporate more complicated checks, like holidays. This query first generates all dates between the two dates, and then lets you filter out all the days that 'don't count'.
In this implementation I filtered out only weekend days, but it's quite easy to add checks for holidays and such.
with
-- YourQuery: I used a stub, but you can use your actual query here, which
-- returns a from date and to date. If you have multiple rows, you can also
-- output some id here, which can be used for grouping in the last step.
YourQuery as
(
select
trunc(sysdate - 7) as FromDate,
trunc(sysdate) as ToDate
from dual),
-- DaysBetween. This returns all the dates from the start date up to and
-- including the end date.
DaysBetween as
(
select
FromDate,
FromDate + level - 1 as DayBetween,
ToDate
from
YourQuery
connect by
FromDate + level - 1 <= ToDate)
-- As a last step, you can filter out all the days you want.
-- This default query only filters out Saturdays and Sundays, but you
-- could add a 'not exists' check that checks against a table with known
-- holidays.
select
count(*)
from
DaysBetween d
where
trim(to_char(DAYINBETWEEN, 'DAY', 'NLS_DATE_LANGUAGE=AMERICAN'))
not in ('SATURDAY', 'SUNDAY');

Custom Postgres function for term dates and times

Lets say I have a large table that just consists of three columns.
Integer id,
timestamp ts,
double value
If I wanted to get the values given a complicated date expression what is the best way to achieve that ?
For example if I wanted to get all the values at anytime on weekend days and only between 18:00 and 8:00 on weekdays and any time on school holidays for the year 2014.
Obviously some of these times are variable and so the solution should be dynamic. I was thinking
of storing a series of date intervals for things like school holidays in another table to check against. However, I would like to create a custom Postgres function to hide some of the complexity.
Does anyone know of similar code or have suggestions ?
Especially dealing with cases like the times above except on weekend logic ?
Thanks
With a holiday table
select *
from
t
left join
holiday on date_trunc('day', t.ts) = holiday.day
where
extract(dow from ts) in (0, 6) -- Weekend
or
(extract(hour from ts) >= 18 and extract(hour from ts) <= 8)
or
holiday.day is not null -- Holiday

SQL query to search by day/month/year/day&month/day&year etc

I have a PostgreSQL database with events. Each event has a datetime or an interval. Common data are stored in the events table and dates are stored in either events_dates (datetime field) or events_intervals (starts_date, ends_date both are date fields).
Sample datetime events
I was born on 1930-06-09
I got my driver's license on 1950-07-12
Christmas is on 1900-12-24 (1900 is reserved for yearly reoccuring events)
Sample interval events
I'll be on vacation from 2011-06-09 till 2011-07-23
Now I have a user that will want to look up these events. They will be able to fill out a form with from and to fields and in those fields they can enter full date, day, month, year, day and month, day and year, month and year in one or both fields.
Sample queries
From May 3 to 2012 December 21 will look for events between May 3 and December 21 whose max year is 2012
From day 3 to day 15 will look for events between the 3rd and 15th day of every month and year
From day 3 will look for events on the 3rd day of every month and year (same if from is empty and to is not)
From May 3 to June will look for events between May 3 and last day of June of every year
etc.
Any tips on how to write a maintanable query (it doesn't necessarily have to be fast)?
Some things that we thought of
write all possible from, to and day/month/year combinations - not maintable
compare dates as strings e.g. input: ____-06-__ where _ is a wildcard - I wouldn't have to generate all possible combinations but this doesn't work for intervals
You can write maintainable queries that additionally are fast by using the pg/temporal extension:
https://github.com/jeff-davis/PostgreSQL-Temporal
create index on events using gist(period(start_date, end_date));
select *
from events
where period(start_date, end_date) #> :date;
select *
from events
where period(start_date, end_date) && period(:start, :end);
You can even use it to disallow overlaps as a table constraint:
alter table events
add constraint overlap_excl
exclude using gist(period(start_date, end_date) WITH &&);
write all possible from, to and day/month/year combinations - not maintable
It's actually more maintainable than you might think, e.g.:
select *
from events
join generate_series(:start_date, :end_date, :interval) as datetime
on start_date <= datetime and datetime < end_date;
But it's much better to use the above-mentioned period type.

SQL Stored Procedure: Business Hours

How can I create a stored procedure that accepts a start and end date.(e.g April 1 - April 30
1.) Get the business days including Saturdays x (a value). +
2.) Get Holidays x (a value)
and return the total.
I'm new to this, I guess it would be a tsql function. hmm.
any help would be appreciated.
Thanks
The simplest solution to this problem is to create a Calendar table that contains a value for every day you might want to consider. You could then add columns that indicate whether it is a business day or a holiday. With that, the problem becomes trivial:
Select ..
From Calendar
Where IsBusinessDay = 1
And Calendar.[Date] Between '2010-04-01' And '2010-04-30'
If you wanted the count of days, you could then do:
Select Sum( Case When IsBusinessDay = 1 Then 1 Else 0 End ) As BusinessDayCount
, Sum( Case When IsHoliday = 1 Then 1 Else 0 End ) As HolidayCount
From Calendar
Where Calendar.[Date] Between '2010-04-01' And '2010-04-30'
http://classicasp.aspfaq.com/date-time-routines-manipulation/how-do-i-count-the-number-of-business-days-between-two-dates.html
First, you will need to store all of the holidays into an independant table (Christmas, Easter, New Year Day, etc. with their respective dates (normally timed at midnight));
Second, you will have to generate, into a temporary table maybe, the dates of the office days, it then excludes the dates contained in the Holidays table.
Third, you may set the office hours to these dates depending on what day it is, if you have different working hours on different day.
That is the algorithm for you to find the appropriate code implementation.
Let me know if this helps!