Related
This should be easy to do but I'm struggling.
I have the below script that calculates a number of business dates before or after a date. I need to change to UDF as I'll be using it in multiple views. It is for Bigquery:
DECLARE Date DATE;
DECLARE DAYS_EXTEND INT64;
DECLARE COUNTER INT64;
SET Date = '2021-12-23';
SET DAYS_EXTEND = 3;
SET COUNTER = DAYS_EXTEND;
BEGIN
WHILE COUNTER > 0 Do
SET COUNTER = COUNTER -1;
SET Date = Date +1;
IF Extract( DAYOFWEEK from Date) in (1,7) or Date in ('2021-01-01','2021-04-02','2021-04-05','2021-05-03','2021-05-31','2021-08-30','2021-12-27','2021-12-28','2022-01-03','2022-04-15','2022-04-18','2022-05-02','2022-06-02','2022-06-03','2022-08-29','2022-12-26','2022-12-27')
Then
BEGIN
SET DAYS_EXTEND = DAYS_EXTEND +1;
SET COUNTER = COUNTER +1;
END;
END IF;
END WHILE;
WHILE COUNTER <0 DO
SET COUNTER = COUNTER +1;
SET Date = Date -1;
IF Extract( DAYOFWEEK from Date) in (1,7) or Date in ('2021-01-01','2021-04-02','2021-04-05','2021-05-03','2021-05-31','2021-08-30','2021-12-27','2021-12-28','2022-01-03','2022-04-15','2022-04-18','2022-05-02','2022-06-02','2022-06-03','2022-08-29','2022-12-26','2022-12-27')
Then
BEGIN
SET DAYS_EXTEND = DAYS_EXTEND - 1;
SET COUNTER = COUNTER - 1;
END;
END IF;
END WHILE;
END;
I'm just not sure how to turn it into a Select statement or if it is possible to do UDF without a Select statement with the loops remaining.
I'd appreciate any help.
For what I understand from your question, you are trying to create a function/procedure that make uses of selects and loops to identify business days. Personally I would avoid complex scripting (usually as last resort). I think It can be achieve by just using selects.
Here is a sample code, that can assist you identifying business days:
DECLARE MyDate DATE;
DECLARE DAYS_RANGE INT64;
SET MyDate = '2021-11-22';
SET DAYS_RANGE = 10;
/*if it fits you, create a table for special dates and holidays*/
create temp table offdays (
nonworkingdays date
);
insert into offdays values('2021-11-23');
insert into offdays values('2021-11-19');
with working_dates as(
select dafter,
case when EXTRACT(DAYOFWEEK FROM dafter) not in (1,7) then 1 else 0 end as isweekdays_after,
case when dafter in (select nonworkingdays from offdays) then 1 else 0 end as isoffdays_after,
dbefore,
case when EXTRACT(DAYOFWEEK FROM dbefore) not in (1,7) then 1 else 0 end as isweekdays_before,
case when dbefore in (select nonworkingdays from offdays) then 1 else 0 end as isoffdays_before
from (
select date_add(MyDate, INTERVAL days_to_count DAY) as dafter,
date_add(MyDate, INTERVAL -days_to_count DAY) as dbefore
from (SELECT days_to_count FROM UNNEST(GENERATE_ARRAY(1, DAYS_RANGE)) AS days_to_count)
)
)
select * from working_dates
If you run it, you will get a main table working_dates with rows equals to the range given and columns that will help you identify weekdays and offdays.
So, You can use this code to create a function or procedure where you can pass parameters and calculate if you either want days after or before and return the days or the count of days filtered by the columns weekdays and offdays.
Take this as a sample function derived from above script:
CREATE TEMP FUNCTION GetBusinessDaysAfter(fnDate Date, days_range INT64)
RETURNS INT64
AS ((
select count(d.dafter) from(
select dafter,
case when EXTRACT(DAYOFWEEK FROM dafter) not in (1,7) then 1 else 0 end as isweekdays_after,
case when dafter in ('2021-11-23') then 1 else 0 end as isoffdays_after,
from (
select date_add(fnDate, INTERVAL days_to_count DAY) as dafter
from (SELECT days_to_count FROM UNNEST(GENERATE_ARRAY(1, days_range)) AS days_to_count)
)) as d
where d.isweekdays_after=1 and d.isoffdays_after=0
));
select GetBusinessDaysAfter('2021-11-22',3)
I can use it to retrieve how many business days will pop up in the next 3 days. Turns out to be just 2 (for the sake of the sample, I put a fixed value in offdays, you can replace it making a reference to your holidays table in your dataset).
For more information about scripting, functions and procedures, here are some useful links:
Data definition language
Working with arrays
Working with dates functions
I'm trying to loop over a query result and combine the result.
I want to loop over the variable called rolling date, which gives out an array of dates with 30 day difference.
DECLARE rollingdate ARRAY<DATE>;
SET rollingdate = ( GENERATE_DATE_ARRAY(CURRENT_DATE(), DATE_SUB(CURRENT_DATE(), INTERVAL 365 DAY), INTERVAL -30 DAY) );
My table is partitioned by DATE, and I'd like to loop over two consecutive dates from the rolling date and union all the results
select *, rollingdate[0]
from table
where date > rollingdate[1] and date < rollingdate[0]
union all
select *, rollingdate[1]
from table
where date > rollingdate[2] and date < rollingdate[1]
How do I achieve this in bigquery? i tried with bigquery scripts, but they don't take subqueries..
You can try using EXECUTE IMMEDIATE:
DECLARE i INT64 DEFAULT 1;
DECLARE dsql STRING DEFAULT '';
DECLARE rollingdate ARRAY<DATE>;
SET rollingdate = ( GENERATE_DATE_ARRAY(CURRENT_DATE(), DATE_SUB(CURRENT_DATE(), INTERVAL 365 DAY), INTERVAL -30 DAY) );
WHILE i <= 2
DO
SET dsql = dsql || " select *, '" || rollingdate[ORDINAL(i)] || "' from table where date > '" || rollingdate[ORDINAL(i+1)] || "' and date < '" || rollingdate[ORDINAL(i)] || "' union all";
SET i = i + 1;
END WHILE;
SET dsql = SUBSTR(dsql, 1, LENGTH(dsql) - LENGTH(' union all'));
EXECUTE IMMEDIATE dsql;
An approach like this should be ok:
with
dates as (
select * from unnest(generate_date_array(current_date, date_sub(current_date(), interval 365 DAY), interval -30 day)) as date
),
date_start_end as (
select lag(date,1) over(order by date asc) as begin_date, date as end_date
from dates
)
select table.*, end_date
from table
inner join date_start_end where date between begin_date and end_date
You might need to make adjustments depending if your date ranges are mean to be inclusive or exclusive.
As mentioned in my comment, any date in table will only be in 1 of your rollingdate intervals. SQL is much more performant when you can do operations on a set and avoid loops.
I am working with BigQuery scripting, I have written a simple WHILE loop which iterates through daily Google Analytics tables and sums the visits, now I'd like to write these results out to a table.
I've gotten as far as creating the table, but I can't capture the value of visits from my SQL query to populate the table. Date works fine, because it is defined outside of the SQL. I tried to DECLARE the value of visits with a new variable, but again this does not work because it's not known outside of the statement.
SET vis = visits;
How can I correctly write my results out to a table?
DECLARE d DATE DEFAULT DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY);
DECLARE pfix STRING DEFAULT REGEXP_REPLACE(CAST(d AS STRING),"-","");
DECLARE vis INT64;
CREATE OR REPLACE TABLE test.looped_results (Date DATE, Visits INT64);
WHILE d > '2019-10-01' DO
SELECT d, SUM(totals.visits) AS visits
FROM `project.dataset.ga_sessions_*`
WHERE _table_suffix = pfix
GROUP BY Date;
SET d = DATE_SUB(d, INTERVAL 1 DAY);
SET vis = visits;
INSERT INTO test.looped_results VALUES (d, visits);
END WHILE;
Update: I also tried an alternative solution, assigning visits to it's own variable, but this produces the same error:
WHILE d > '2019-10-01' DO
SET vis_count = (SELECT SUM(totals.visits) AS visits
FROM `mindful-agency-136314.43786551.ga_sessions_*`
WHERE _table_suffix = pfix);
INSERT INTO test.looped_results VALUES (d, vis_count);
SET d = DATE_SUB(d, INTERVAL 1 DAY);
END WHILE;
Results:
In my results I see the correct number of rows created, with the correct dates, but the value of visits for each is the value for the most recent day.
I would also move INSERT INTO outside of the WHILE loop by collecting result into result variable (along with few other minor changes) as in below example
DECLARE d DATE DEFAULT DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY);
DECLARE pfix STRING;
DECLARE vis_count INT64;
DECLARE result ARRAY<STRUCT<vis_date DATE, vis_count INT64>> DEFAULT [];
CREATE OR REPLACE TABLE test.looped_results (Date DATE, Visits INT64);
WHILE d > '2019-10-01' DO
SET pfix = REGEXP_REPLACE(CAST(d AS STRING),"-","");
SET vis_count = (SELECT SUM(totals.visits) AS visits
FROM `project.dataset.ga_sessions_*`
WHERE _table_suffix = pfix);
SET result = ARRAY_CONCAT(result, [STRUCT(d, vis_count)]);
SET d = DATE_SUB(d, INTERVAL 1 DAY);
END WHILE;
INSERT INTO test.looped_results SELECT * FROM UNNEST(result);
Note: I hope your example is for scripting learning purpose and not for production as whenever possible we should stick with set based processing which can be easily done in your case
Here is a better way which is faster and without using a loop.
Basically, you form an array of suffix and do SELECT/INSERT in single query:
DECLARE date_range ARRAY<DATE> DEFAULT
GENERATE_DATE_ARRAY(DATE '2019-10-01', DATE '2019-10-10', INTERVAL 1 DAY);
DECLARE suffix_array ARRAY<STRING>
DEFAULT (SELECT ARRAY_AGG(REGEXP_REPLACE(CAST(dates AS STRING),"-",""))
FROM UNNEST(date_range) dates);
CREATE OR REPLACE TABLE test.looped_results (Date DATE, Visits INT64);
INSERT INTO test.looped_results
SELECT Date, SUM(totals.visits)
FROM `project.dataset.ga_sessions_*`
WHERE _table_suffix in UNNEST(suffix_array);
GROUP BY Date;
Actually, you need to update the pfix variable in there. Also, it is a good idea to instantiate the visits. Finally, your GROUPBY doesn't necessarily need a dimension if you are providing it with a pfix constraint.
This should do it:
DECLARE d DATE DEFAULT DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY);
DECLARE pfix STRING DEFAULT REGEXP_REPLACE(CAST(d AS STRING),'-','');
DECLARE visits int64;
SET visits = 0;
CREATE OR REPLACE TABLE project.dataset.looped_results (Date DATE, Visits INT64);
WHILE d > '2019-10-01' DO
SET visits = (SELECT SUM(totals.visits) FROM `project.dataset.ga_sessions_*` WHERE _table_suffix = pfix);
SET d = DATE_SUB(d, INTERVAL 1 DAY);
SET pfix = REGEXP_REPLACE(CAST(d AS STRING),"-","");
INSERT INTO dataset.looped_results VALUES (d, visits);
END WHILE;
Hope it helps.
Having reviewed my code (several times!) I realized that I wasn't refreshing the variable which transforms the data into the table prefix within the loop.
Here is a working version of the script, where I set pfix at the end of the loop:
DECLARE d DATE DEFAULT DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY);
DECLARE pfix STRING DEFAULT REGEXP_REPLACE(CAST(d AS STRING),"-","");
DECLARE vis_count INT64;
CREATE OR REPLACE TABLE test.looped_results (Date DATE, Visits INT64);
WHILE d > '2019-10-01' DO
SET vis_count = (SELECT SUM(totals.visits) AS visits
FROM `project.dataset.ga_sessions_*`
WHERE _table_suffix = pfix);
INSERT INTO test.looped_results VALUES (d, vis_count);
SET d = DATE_SUB(d, INTERVAL 1 DAY);
SET pfix = REGEXP_REPLACE(CAST(d AS STRING),"-","");
END WHILE;
I would actually not use a while loop but rather a group by
SELECT date, SUM(totals.visits) AS visits
FROM `mindful-agency-136314.43786551.ga_sessions_*`
GROUP BY Date;
It will give you your results as the table that you want, you don't need to loop on your table.
Depending on your set up but you can set the query to be ran every day so every new day you will have the new values
This code works when run as SQL Query in Microsoft SQL Server Management Studio 2008 but when is set as Job step then works only second condition (update when is not last day of month). What is wrong with this code?
--set next invoice date
declare #data nvarchar(10) --invoice date
set #data = CONVERT (date, GETDATE());
update table
set invoice_date = case when day(DATEADD(day,1,#data)) = 1 then --is last day of month
(SELECT convert(date,DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+1,0)))) -- set inovice date as last day of next month
else --is not last day of month
(select DATEADD(MM,1,#data)) --add one month to inovice date
end
where status = 'current' and invoice_date = #data -- only for current inovices
Try this. First condition will work on every month last day only.
--set next invoice date
DECLARE #data NVARCHAR(10) --invoice date
SET #data = CONVERT (DATE, GETDATE());
UPDATE table
SET invoice_date = CASE WHEN DAY(DATEADD(DAY,1,#data)) = 1
THEN --is last day of month
CAST( DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,#data)+2,0)) AS DATE) -- set inovice date as last day of next month
ELSE --is not last day of month
(DATEADD(MM,1,#data)) --add one month to inovice date
END
WHERE status = 'current' AND invoice_date = #data -- only for current inovices
I am sure somebody has worked out to simulate Excel networkdays function in other programming language.
I'd appreciate if you could share.
Thanks
The idea is to calculate the weekdays between the start of each date's week then applying various offsets.
Find the number of days between the Saturday before each date
Divide by 7 and multiply by 5 to get the number of weekdays
Offset the total for whether the start date is after the end date
Offset again for whether the start is after the end and the start is a Saturday
Again for whether the start is after and the end is Sunday
Again for whether the start is not after and the start is a Sunday
Again for whether the start is not after and the end is a Saturday
Add some random dates into a table.
declare #t table ([start] datetime, [end] datetime)
insert into #t values ('2088-01-14 11:56:23','2011-11-10 03:34:09')
insert into #t values ('2024-09-24 10:14:29','2087-09-16 15:52:06')
Then calcuate NETWORKDAYS for those dates.
select [start],[end]
,((datediff(day,0,[end])-datepart(dw,[end]))-(datediff(day,0,[start])-datepart(dw,[start])))/7*5 --[weekdays]
+ datepart(dw,[end]) - datepart(dw,[start]) --[weekday diff]
+ case when datediff(day,0,[start]) > datediff(day,0,[end]) then -1 else 1 end --[start after]
+ case when datediff(day,0,[start]) > datediff(day,0,[end]) and datepart(dw,[start]) = 7 then 1 else 0 end --[start after and start saturday]
+ case when datediff(day,0,[start]) > datediff(day,0,[end]) and datepart(dw,[end]) = 1 then 1 else 0 end --[start after and end sunday]
+ case when datediff(day,0,[start]) <= datediff(day,0,[end]) and datepart(dw,[start]) = 1 then -1 else 0 end --[start not after and start sunday]
+ case when datediff(day,0,[start]) <= datediff(day,0,[end]) and datepart(dw,[end]) = 7 then -1 else 0 end --[start not after and end saturday]
as [networkdays]
from #t
A SQL solution as per SO question "Equivalent of Excel’s NETWORKDAYS function with Jet ADO"
Hope this helps someone out there but this works for me. Requires you to create a dbo.FederalHolidays table (which can be easily populated with online date sources).
Cleanest way I could come up with that is easily scalable.
--Simulate Excel Formula =NETWORKDAYS() excludes weekend days and federal holidays. Requires a Federal Holiday Table, easy to create.
DECLARE #d1 date, #d2 date
SET #d1 = '4/12/2019'
SET #d2 = '4/23/2019'
SELECT
DATEDIFF(dd,#d1,#d2) +1 --First calculate regular date difference +1 to count the start date; does not exclude weekends/holidays.
- (SELECT COUNT(*) --Calculate number of holidays between the date range and subtract it from DATEDIFF().
FROM [dbo].[FederalHolidays]
WHERE DATE BETWEEN #d1 AND #d2)
- (SELECT (DATEDIFF(wk, #d1, #d2) * 2) --Calculate number of weekend days between the date range and subtract it from DATEDIFF().
+(CASE WHEN DATENAME(dw, #d1) = 'Sunday' THEN 1 ELSE 0 END)
+(CASE WHEN DATENAME(dw, #d2) = 'Saturday' THEN 1 ELSE 0 END)) as NetWorkDays
For what it's worth, I created the following function for mysql that assumes Mon-Fri are business days:
DROP FUNCTION IF EXISTS BusinessDays;
DELIMITER //
CREATE FUNCTION BusinessDays (startDate DATE, endDate DATE)
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE startWeekDay INT;
DECLARE allDays INT;
DECLARE fullWeekCount INT;
DECLARE remainderDays INT;
DECLARE maxPossibleRemainderWeekendDays INT;
DECLARE soloSundays INT;
DECLARE totalBusinessDays INT;
SET startWeekDay = WEEKDAY(startDate);
SET allDays = ABS(DATEDIFF(endDate, startDate)) + 1;
SET fullWeekCount = FLOOR(allDays/7);
SET remainderDays = allDays - (fullWeekCount * 7);
SET maxPossibleRemainderWeekendDays = ROUND(2*(startWeekDay+remainderDays-6)/(ABS(2*(startWeekDay+remainderDays-6))+1))+1;
SET soloSundays = ROUND(2*(startWeekDay-6)/(ABS(2*(startWeekDay-6))+1))+1;
SET totalBusinessDays = allDays - (fullWeekCount * 2) - maxPossibleRemainderWeekendDays + soloSundays;
RETURN totalBusinessDays;
END //
DELIMITER ;
Perhaps this will also help, I created a formula (in Excel) that will simulate the NETWORKDAYS function:
= 1 + ( ( B1 - A1) * 5 - ( WEEKDAY(A1) - WEEKDAY(B1) ) * 2 ) / 7 + IF(A1<=B1,IF(WEEKDAY(B1)=7,-1,0) + IF(WEEKDAY(A1)=1,-1,0), IF(WEEKDAY(B1)<>1,-1,0) + IF(WEEKDAY(A1)<>7,-1,0) )
NOTE: WEEKDAY() has Sunday as 0 to Saturday as 6
I have been looking for this capability for quite some time, so went ahead and just created it on my own.
Usage: This is a function you can create in SQL. You can easily modify this to work outside a function if needed, but I prefer functions to take care of these types of calculations.
I added quite a few comments in case anyone was wondering how it worked. What this does is take two inputs:
#startDate - the beginning date you want to add workday to.
#addDays - the whole number of days you want to add to the #startDate.
Calculation process:
It loops repeatedly until the # of working days has been reached. For example, if you enter 365 days, it will cycle around 500 times before it's able to predict the # of working days to add. I've added #weekendCount in case anyone needs to know the # of weekend days excluded before reaching the final end date.
Once completed, the integer #recCounter essentially is the # of days that must be added to the #startDate before the number of working days is reached. I am sure someone can write this better than I, or perhaps someone can make use of this. Hope this helps! :)
CREATE FUNCTION [dbo].[addNetworkDays](#startDate AS DATETIME, #addDays AS Int)
RETURNS DATETIME
AS
BEGIN
DECLARE #recCounter Int
SET #recCounter = 0
DECLARE #weekendCount Int
SET #weekendCount = 0
DECLARE #workdayCount Int
SET #workdayCount = 0
DECLARE #newDate DateTime
WHILE #addDays > #workdayCount
BEGIN
-- Add another day to the start date
SET #recCounter = #recCounter + 1
-- Cumuluate the weekend vs. workday counter, based on the day of the week. This loop will repeat until #workdayCount has reached the #addDays.
-- Note that #weekendCount is not used in any capacity, can be used if you need to know how many weekend days there are.
IF DATEPART(dw, DATEADD(d, #recCounter, #startDate)) IN (1, 7) SET #weekendCount = #weekendCount + 1
IF DATEPART(dw, DATEADD(d, #recCounter, #startDate)) IN (2, 3, 4, 5, 6) SET #workdayCount = #workdayCount + 1
END
-- At this point, the script has completed the cycle, stopping when the detected # of workdays has reached the count of days the user specified.
-- Calculate the new date, based on the # of cycles *days* detected above.
SET #newDate = DATEADD(d, #recCounter, #startDate)
-- If the new/adjusted date falls on a Saturday or Sunday, add additional days to compensate.
IF DATEPART(dw, #newDate) = 1 SET #newDate = DATEADD(d, 1, #newDate)
IF DATEPART(dw, #newDate) = 7 SET #newDate = DATEADD(d, 1, #newDate)
RETURN CAST(#newDate AS DATETIME)
NETWORKDAYS for Australia
WITH PH_NET_WORKDAY AS
(
----BUILD UNDERLYING WORKDAY DATA TABLE
SELECT
DT,
STATE,
CASE
WHEN WORKDAY-PUBLIC_HOLIDAY <0
THEN 0
ELSE WORKDAY-PUBLIC_HOLIDAY
END AS WORKDAY
FROM
(
SELECT
DT,
STATE,
WORKDAY,
----PUBLIC HOLIDAY INFORMATION HERE
CASE
WHEN STATE = 'NSW'
THEN
CASE
WHEN DT IN (
'01-Oct-2018',
'25-Dec-2018',
'26-Dec-2018',
'01-Jan-2019',
'28-Jan-2019',
'19-Apr-2019',
'20-Apr-2019',
'21-Apr-2019',
'22-Apr-2019',
'25-Apr-2019',
'10-Jun-2019',
'07-Oct-2019',
'25-Dec-2019',
'26-Dec-2019'
)
THEN 1
ELSE 0
END
WHEN STATE = 'SA'
THEN
CASE
WHEN DT IN (
'01-Oct-2018',
'24-Dec-2018',
'25-Dec-2018',
'26-Dec-2018',
'31-Dec-2018',
'01-Jan-2019',
'28-Jan-2019',
'11-Mar-2019',
'19-Apr-2019',
'20-Apr-2019',
'22-Apr-2019',
'25-Apr-2019',
'10-Jun-2019',
'07-Oct-2019',
'24-Dec-2019',
'25-Dec-2019',
'26-Dec-2019'
)
THEN 1
ELSE 0
END
WHEN STATE = 'QLD'
THEN
CASE
WHEN DT IN (
'01-Oct-2018',
'25-Dec-2018',
'26-Dec-2018',
'01-Jan-2019',
'28-Jan-2019',
'19-Apr-2019',
'20-Apr-2019',
'21-Apr-2019',
'22-Apr-2019',
'25-Apr-2019',
'06-May-2019',
'07-Oct-2019',
'25-Dec-2019',
'26-Dec-2019'
)
THEN 1
ELSE 0
END
WHEN STATE = 'VIC'
THEN
CASE
WHEN DT IN (
'28-Sep-2018',
'06-Nov-2018',
'25-Dec-2018',
'26-Dec-2018',
'01-Jan-2019',
'28-Jan-2019',
'11-Mar-2019',
'19-Apr-2019',
'20-Apr-2019',
'21-Apr-2019',
'22-Apr-2019',
'25-Apr-2019',
'10-Jun-2019',
'05-Nov-2019',
'25-Dec-2019',
'26-Dec-2019'
)
THEN 1
ELSE 0
END
WHEN STATE = 'TAS'
THEN
CASE
WHEN DT IN (
'25-Dec-2018',
'26-Dec-2018',
'01-Jan-2019',
'28-Jan-2019',
'11-Mar-2019',
'19-Apr-2019',
'22-Apr-2019',
'23-Apr-2019',
'25-Apr-2019',
'10-Jun-2019',
'25-Dec-2019',
'26-Dec-2019'
)
THEN 1
ELSE 0
END
ELSE 0
END AS PUBLIC_HOLIDAY
FROM
(
SELECT
DT.*,
ST.*
FROM
(
SELECT
TRUNC (TO_DATE('01-JAN-2021','DD-MON-YYYY') - ROWNUM) AS DT,
TRIM(TO_CHAR( TRUNC (TO_DATE('01-JAN-2021','DD-MON-YYYY') - ROWNUM) , 'DAY')) AS WEEK_DAY,
CASE
WHEN TRIM(TO_CHAR( TRUNC (TO_DATE('01-JAN-2021','DD-MON-YYYY') - ROWNUM) , 'DAY')) = 'SATURDAY'
THEN 0
WHEN TRIM(TO_CHAR( TRUNC (TO_DATE('01-JAN-2021','DD-MON-YYYY') - ROWNUM) , 'DAY')) = 'SUNDAY'
THEN 0
----BUILD STATE PUBLIC HOLIDAY DATE SET HERE, WE CAN EXCLUDE PUBLIC HOLIDAYS
ELSE 1
END AS WORKDAY
FROM DUAL CONNECT BY ROWNUM < (365.25*8+1)
) DT
,
(
SELECT 'NSW' AS STATE FROM DUAL
UNION
SELECT 'QLD' AS STATE FROM DUAL
UNION
SELECT 'SA' AS STATE FROM DUAL
UNION
SELECT 'ACT' AS STATE FROM DUAL
UNION
SELECT 'VIC' AS STATE FROM DUAL
UNION
SELECT 'ACT' AS STATE FROM DUAL
) ST
)
)
),
----A SAMPLE DATA SET FOR SAME DATES BETWEEN DIFFERENT STATE TO TEST PUBLIC HOLIDAY DIFFERENCES
SAMPLE_DATA AS
(
SELECT
'01-FEB-2019' AS D1,
'11-FEB-2019' AS D2,
'NSW' AS STATE
FROM DUAL
UNION
SELECT
'01-FEB-2019' AS D1,
'11-FEB-2019' AS D2,
'SA' AS STATE
FROM DUAL
UNION
SELECT
'19-APR-2019' AS D1,
'26-APR-2019' AS D2,
'NSW' AS STATE
FROM DUAL
)
----SELECT WORKDAYS FROM PH TABLE AND INSERT INTO SAPLE TABLE
SELECT
SAMPLE_DATA.*,
(
SELECT SUM(WORKDAY)
FROM PH_NET_WORKDAY
WHERE
STATE = SAMPLE_DATA.STATE
AND DT>=SAMPLE_DATA.D1
AND DT<=SAMPLE_DATA.D2
)
AS WORKDAYS
FROM SAMPLE_DATA