Skip to main content
Question

Calulating date after specific number of workdays

  • November 27, 2022
  • 4 replies
  • 9 views

MateuszDudek

Hello,

I'm trying to calculate date after N working days (for now we ignore public holidays - it's only about excluding weekends). Unfortunately we're doing that in ETL part of Celonis Process Mining which uses Vertica SQL (10.1.x version) but without some specific analytic functions like LEAD/LAD which could solve my issue, I also cannot used any king of custom functions, so only simple SQL is viable here.

I've seen these links, however they cannot solve my problem:
A) https://forum.vertica.com/discussion/239801/counting-days-week-days-and-weekend-days-across-the-years (I want to calculate for every row in the table, not single value)
B)https://forum.vertica.com/discussion/239605/add-days-to-a-date-excluding-sat-and-sun

I've also tried generate list of days between min and max dates in the table, however I cannot find it useful without LEAD/LAG functions. Do you have some ideas?

CODE:
Create Table "TEST_TBL" AS (
SELECT the_date
FROM
(SELECT ts::date AS the_date
FROM
(
SELECT MIN(CPUDT)::TIMESTAMP AS tm FROM TABLE UNION --Get Min date from TABLE
SELECT MAX(CPUDT)::TIMESTAMP AS tm FROM TABLE --Get Max date from TABLE
) AS t TIMESERIES ts AS '1 DAY' OVER (ORDER BY tm)) foo --Fill the gapes by generating dates between MIN & MAX
WHERE DAYOFWEEK(the_date) NOT IN (1,7) --Do that only for working days

INPUT:

Some_Date

2022-11-28
2022-11-27

DESIRED OUTPUT
-------------- | ------------------------------ |

Some_Date Some_Date+3 workDays
2022-11-28 2022-12-01
2022-11-25 2022-12-30

Best Regards,
Mateusz Dudek

4 replies

Bryan_H
Forum|alt.badge.img+2
  • Participating Frequently
  • November 28, 2022

How about something like this (if you can't create function, you only need the SQL from the RETURN line):
CREATE OR REPLACE FUNCTION AddBusinessDays(ts TIMESTAMP, days INT) RETURN TIMESTAMP
AS BEGIN
RETURN TIMESTAMPADD('day', (days % 5)::int + CASE ((1 + DAYOFWEEK_ISO(ts) + (days % 5)) % 7) WHEN 0 THEN 2 WHEN 1 THEN 1 ELSE 0 END, TIMESTAMPADD('week', (days / 5)::int, ts));
END;
Adapted from T-SQL example at https://stackoverflow.com/questions/5471524/add-business-days-to-date-in-sql-without-loops


marcothesane
Forum|alt.badge.img+1
  • Participating Frequently
  • November 28, 2022

To cater for holidays, too - and, by adding some sort of a geography/region key, for local holidays, create a calendar table, with a counter set to 1 in case the row is a workday, 0 otherwise.
For this example, I just create a calendar where Saturday and Sunday are non-working days, and limit to 3 months.

DROP TABLE IF EXISTS cal;

CREATE TABLE cal AS
WITH
cal AS (
  SELECT
    tm::DATE as dt
  , CASE DAYOFWEEK(dt)
      WHEN 1 THEN 0
      WHEN 7 THEN 0
      ELSE 1
    END AS wkdaycounter
  FROM (
               SELECT TIMESTAMP '2022-10-01'
     UNION ALL SELECT TIMESTAMP '2022-12-31'
  ) limits(ts)
  TIMESERIES tm AS '1 DAY' OVER(ORDER BY ts) 
);

Then, you can range-join your input data over the from-date, so that the calendar date is greater than the from-date - and limit to, say the double number of workdays to add. On that range-join, apply a running sum on the week day counter coming from the calendar. Pack this last query again as a subquery (a Common Table Expression in my case), and finally filter by the result of the running sum equal to the number of workdays you want. I left all intermediate columns in the report so you can see what happened.,

WITH
indata(dt, wkdays) AS (
             SELECT DATE '2022-11-26', 3
   UNION ALL SELECT DATE '2022-11-27', 3
   UNION ALL SELECT DATE '2022-11-28', 3
)  
,
wrkdayrunsum AS (
  SELECT
    indata.dt
  , indata.wkdays
  , cal.dt as after_n_wkdays
  , SUM(wkdaycounter) OVER(PARTITION BY indata.dt ORDER BY cal.dt) AS wdaycount
  FROM indata
  JOIN cal ON cal.dt > indata.dt AND cal.dt <= indata.dt + wkdays * 2
) 
SELECT
  *
FROM wrkdayrunsum
WHERE wdaycount = wkdays
;
-- out      dt     | wkdays | after_n_wkdays | wdaycount 
-- out ------------+--------+----------------+-----------
-- out  2022-11-27 |      3 | 2022-11-30     |         3
-- out  2022-11-26 |      3 | 2022-11-30     |         3
-- out  2022-11-28 |      3 | 2022-12-01     |         3

VValdar
Forum|alt.badge.img+1
  • Participating Frequently
  • November 29, 2022

I think you can modify this for this:
TIMESTAMPADD('week', (days / 5)::int, ts));
TIMESTAMPADD('week', days // 5, ts));

It's better but there's still a day that don't output like expected.


Kedis1999
  • New Participant
  • December 6, 2022
  1. Calculating Date After Specific Number of Workdays
    The formula for calculating the date after a specific number of workdays is:
    Date (Workday + 1) / 2
    For example: If you want to calculate the date after 5 days, then you would use the following formula:
    (5 + 1) / 2 4/2 2
    So the date after two weeks is February 6th.
    1. Calculating Date Before Specific Number of Workdays
      The formula for calculating date before a specific number of work days is:
      Date Workday - (Workday + 1)/2
      For example: If we wanted to calculate the date before five days, we would use the following formula.
      (5 - (5+1)) / 2 4/ 2 2
      So the day before two weeks is January 25th.