Counting Biz days for each row of database - sql

I have a table with each row containing a start and end date with timestamp format and need to filter them by the number of business days between the start and end date.
Based on some of the solutions posted here, I created a separate table with all days and marked them with a boolean field like this:
CREATE TABLE tbl_holiday (h_date TIMESTAMP, is_holiday BOOLEAN)
Is it possible to write a query that filters by count days between start_date and date_date that has _is_holiday as False?
My database is Impala.

You would typically join the original table with the holiday table with inequality conditions on the start and end date, aggregate, and finally filter in a having clause by the sum of business days against your target value:
select t.id, t.start_date, t.end_date
from mytable t
inner join tbl_holiday h on h.hdate between t.start_date and t.end_date
group by t.id, t.start_date, t.end_date
having sum(cast(is_holiday as int)) = :no_of_business_days

Related

Max date removal and uaing last buisness date in oracle sql

Select max( date) from t1 t where a.calendar_ date= t.date
This query creating issue when month ends on weekend or on holiday. This query joins with another table so I need a query by using which I can get correct date.
"On holiday" is hard because they are regional and change from year to year. You should keep a table full of dates that are holidays so that you can exclude them with something like:
Select max(date)
from t1 t
LEFT JOIN holidays h on t.date = h.date
where
h.date IS NULL AND --not a holiday
(1 + TRUNC (t.date) - TRUNC (t.date, 'IW')) NOT IN (6,7) AND -- not a weekend, regardless of region
a.calendar_date= t.date
ps; I've no idea where your a alias came from

PL-SQL query to calculate customers per period from start and stop dates

I have a PL-SQL table with a structure as shown in the example below:
I have customers (customer_number) with insurance cover start and stop dates (cover_start_date and cover_stop_date). I also have dates of accidents for those customers (accident_date). These customers may have more than one row in the table if they have had more than one accident. They may also have no accidents. And they may also have a blank entry for the cover stop date if their cover is ongoing. Sorry I did not design the data format, but I am stuck with it.
I am looking to calculate the number of accidents (num_accidents) and number of customers (num_customers) in a given time period (period_start), and from that the number of accidents-per-customer (which will be easy once I've got those two pieces of information).
Any ideas on how to design a PL-SQL function to do this in a simple way? Ideally with the time periods not being fixed to monthly (for example, weekly or fortnightly too)? Ideally I will end up with a table like this shown below:
Many thanks for any pointers...
You seem to need a list of dates. You can generate one in the query and then use correlated subqueries to calculate the columns you want:
select d.*,
(select count(distinct customer_id)
from t
where t.cover_start_date <= d.dte and
(t.cover_end_date > d.date + interval '1' month or t.cover_end_date is null)
) as num_customers,
(select count(*)
from t
where t.accident_date >= d.dte and
t.accident_date < d.date + interval '1' month
) as accidents,
(select count(distinct customer_id)
from t
where t.accident_date >= d.dte and
t.accident_date < d.date + interval '1' month
) as num_customers_with_accident
from (select date '2020-01-01' as dte from dual union all
select date '2020-02-01' as dte from dual union all
. . .
) d;
If you want to do arithmetic on the columns, you can use this as a subquery or CTE.

Sum values based on start and end dates in another table

i am trying to do a calculation in BigQuery using SQL and I have no idea about how to go about this.
I have two tables:
Tablename: Periods
id INTEGER
start_date DATE
end_date DATE
And another table
Tablename: Data
date DATE
id INTEGER
value FLOAT
And what I want to do is to create a Query that can sum together the value for each id, and timerange (start_date to end_date) in the Periods table. In this case the Data table can have values for id's that are outside of the timerange in the Periods table so I need the Query to limit the summation to just from - to the start_date and end_date.
Hope someone can point me in the right direction on this.
Consider using subquery:
SELECT
id,
(SELECT SUM(value)
FROM Data
WHERE Data.id = Periods.id
AND Data.date >= Periods.start_date
AND Data.date <= Periods.end_date
) AS sums
FROM Periods

Redshift - Adding dates (month interval) between two dates

Using Amazon Redshift.
Also have a dates table with all calendar dates that can be utilized.
Question: How can I take a start timestamp (created_at) and end timestamp (ended_at) and add a column that adds 1 month to the start timestamp until the end timestamp.
I have a table with:
user_id,
plan_id,
created_at,
ended_at, (can be null)
So if I had a created_at timestamp of 2019-07-11, I would have a column with additional rows for 2019-08-11, 2019-09-11, 2019-10-11, etc. The goal is to associate the monthly amounts paid by a user to the dates when starting with only a start and end date.
EDIT:
I used the below query which works when an ended_at timestamp is present, however, when it is null, I need to have the next month populated until an ended_at timestamp is present.
select
ps.network_id,
ps.user_id,
ps.plan_id,
ps.created_at,
extract('day' from ps.created_at) as extract_day,
d.calendar_date,
ps.archived_at as ended_at,
ps.application_fee_percent,
pp.amount,
pp.interval,
pp.name
from payments_subscriptions ps
left outer join dates d on extract('day' from date_trunc('day',d.calendar_date)) = extract('day' from ps.created_at) AND date_trunc('day',d.calendar_date) >= date_trunc('day',ps.created_at) AND date_trunc('day',d.calendar_date) < date_trunc('day',ps.archived_at)
left outer join payments_plans pp on ps.plan_id = pp.id
where ps.network_id = '1318990'
and ps.user_id = '2343404'
order by 3,6 desc
output from above query - subscription with null ended_at needs to continue until ended_at is present
Use dateadd function for increasing time/date in timestamp
https://docs.aws.amazon.com/redshift/latest/dg/r_DATEADD_function.html
For increasing one month use this:
DATEADD(month, 1, CURRENT_TIMESTAMP)
For anyone looking for a potential solution, I ended up joining my dates table in this fashion:
LEFT OUTER JOIN dates d ON extract('day' FROM date_trunc('day',d.calendar_date)) = extract('day' FROM payments_subscriptions.created_at)
AND date_trunc('day',d.calendar_date) >= date_trunc('day',payments_subscriptions.created_at)
AND date_trunc('day',d.calendar_date) < date_trunc('day',getdate())
and this where clause:
WHERE (calendar_date < date_trunc('day',payments_subscriptions.archived_at) OR payments_subscriptions.archived_at is null)

Calculating business days in Teradata

I need help in business days calculation.
I've two tables
1) One table ACTUAL_TABLE containing order date and contact date with timestamp datatypes.
2) The second table BUSINESS_DATES has each of the calendar dates listed and has a flag to indicate weekend days.
using these two tables, I need to ensure business days and not calendar days (which is the current logic) is calculated between these two fields.
My thought process was to first get a range of dates by comparing ORDER_DATE with TABLE_DATE field and then do a similar comparison of CONTACT_DATE to TABLE_DATE field. This would get me a range from the BUSINESS_DATES table which I can then use to calculate count of days, sum(Holiday_WKND_Flag) fields making the result look like:
Order# | Count(*) As DAYS | SUM(WEEKEND DATES)
100 | 25 | 8
However this only works when I use a specific order number and cant' bring all order numbers in a sub query.
My Query:
SELECT SUM(Holiday_WKND_Flag), COUNT(*) FROM
(
SELECT
* FROM
BUSINESS_DATES
WHERE BUSINESS.Business BETWEEN (SELECT ORDER_DATE FROM ACTUAL_TABLE
WHERE ORDER# = '100'
)
AND
(SELECT CONTACT_DATE FROM ACTUAL_TABLE
WHERE ORDER# = '100'
)
TEMP
Uploading the table structure for your reference.
SELECT ORDER#, SUM(Holiday_WKND_Flag), COUNT(*)
FROM business_dates bd
INNER JOIN actual_table at ON bd.table_date BETWEEN at.order_date AND at.contact_date
GROUP BY ORDER#
Instead of joining on a BETWEEN (which always results in a bad Product Join) followed by a COUNT you better assign a bussines day number to each date (in best case this is calculated only once and added as a column to your calendar table). Then it's two Equi-Joins and no aggregation needed:
WITH cte AS
(
SELECT
Cast(table_date AS DATE) AS table_date,
-- assign a consecutive number to each busines day, i.e. not increased during weekends, etc.
Sum(CASE WHEN Holiday_WKND_Flag = 1 THEN 0 ELSE 1 end)
Over (ORDER BY table_date
ROWS Unbounded Preceding) AS business_day_nbr
FROM business_dates
)
SELECT ORDER#,
Cast(t.contact_date AS DATE) - Cast(t.order_date AS DATE) AS #_of_days
b2.business_day_nbr - b1.business_day_nbr AS #_of_business_days
FROM actual_table AS t
JOIN cte AS b1
ON Cast(t.order_date AS DATE) = b1.table_date
JOIN cte AS b2
ON Cast(t.contact_date AS DATE) = b2.table_date
Btw, why are table_date and order_date timestamp instead of a date?
Porting from Oracle?
You can use this query. Hope it helps
select order#,
order_date,
contact_date,
(select count(1)
from business_dates_table
where table_date between a.order_date and a.contact_date
and holiday_wknd_flag = 0
) business_days
from actual_table a