how to calculate number of working days in pure sql [duplicate] - sql

I have two date columns and trying to measure days between the two dates excluding weekends. I'm getting a negative number and need help solving.
Table
CalendarDate DayNumber FirstAssgn FirstCnt DayNumber2 Id BusinessDays
5/21/2017 Sunday 5/21/17 5/21/17 Sunday 1 -1
Query:
TRUNC(TO_DATE(A.FIRST_CONTACT_DT, 'DD/MM/YYYY')) - TRUNC(TO_DATE(A.FIRST_ASSGN_DT, 'DD/MM/YYYY'))
- ((((TRUNC(A.FIRST_CONTACT_DT,'D'))-(TRUNC(A.FIRST_ASSGN_DT,'D')))/7)*2)
- (CASE WHEN TO_CHAR(A.FIRST_ASSGN_DT,'DY','nls_date_language=english') ='SUN' THEN 1 ELSE 0 END)
- (CASE WHEN TO_CHAR(A.FIRST_CONTACT_DT,'DY','nls_date_language=english')='SAT' THEN 1 ELSE 0 END)
- (SELECT COUNT(1) FROM HUM.CALENDAR CAL
WHERE 1=1
AND CAL.CALENDAR_DATE >= A.FIRST_ASSGN_DT
AND CAL.CALENDAR_DATE < A.FIRST_CONTACT_DT
--BETWEEN A.FIRST_ASSGN_DT AND A.FIRST_CONTACT_DT
AND CAL.GRH_HOLIDAY_IND = 'Y'
) AS Business_Days
Looks like below piece needs editing...
- (CASE WHEN TO_CHAR(A.FIRST_ASSGN_DT,'DY','nls_date_language=english')='SUN' THEN 1 ELSE 0 END)

Adapted from my answer here:
Get the number of days between the Mondays of both weeks (using TRUNC( datevalue, 'IW' ) as an NLS_LANGUAGE independent method of finding the Monday of the week) then add the day of the week (Monday = 1, Tuesday = 2, etc., to a maximum of 5 to ignore weekends) for the end date and subtract the day of the week for the start date. Like this:
SELECT ( TRUNC( end_date, 'IW' ) - TRUNC( start_date, 'IW' ) ) * 5 / 7
+ LEAST( end_date - TRUNC( end_date, 'IW' ) + 1, 5 )
- LEAST( start_date - TRUNC( start_date, 'IW' ) + 1, 5 )
AS WeekDaysDifference
FROM your_table

With RANGE_TEMP as (
SELECT
STARTPERIOD start_date,
ENDPERIOD end_date
FROM
TABLE_DATA -- YOUR TABLE WITH ALL DATA DATE
), DATE_TEMP AS (
SELECT
(start_date + LEVEL) DATE_ALL
FROM
RANGE_TEMP
CONNECT BY LEVEL <= (end_date - start_date)
), WORK_TMP as (
SELECT
COUNT(DATE_ALL) WORK_DATE
FROM
DATE_TEMP
WHERE
TO_CHAR(DATE_ALL,'D', 'NLS_DATE_LANGUAGE=ENGLISH') NOT IN ('1','7')
), BUSINESS_TMP as (
SELECT
COUNT(DATE_ALL) BUSINESS_DATE
FROM
DATE_TEMP
WHERE
TO_CHAR(DATE_ALL,'D', 'NLS_DATE_LANGUAGE=ENGLISH') IN ('1','7')
)
SELECT
L.WORK_DATE,
H.BUSINESS_DATE
FROM
BUSINESS_TMP H,
WORK_TMP L
;

Related

I have date function to create for entering date and to create random time i want to combine both of them

hi dear i have a query which create dates for specic lenght which is mentioned and a query which is create random time want to join both for date generating is this
It create dates for specific lenght and insert into table query is
insert into calendar_date2
with dates as (
select date'2019-12-31'+level dt
from dual
connect by level <= 365
)
select dt,
case
when to_char(dt, 'fmday') in ('sunday')
then 'N' else 'Y'
end
from dates
;
what it will do is enter CALENDER_DATE2 all dates mentioned on connect by level
2nd query is to genrate random time
with test (start_date) as
(select to_date('01.01.2021 07:45', 'dd.mm.yyyy hh24:mi') from dual
)
select start_date + round(dbms_random.value(1, 15)) / (24 * 60) result
from test
connect by level <= 15
I want to write both query work together what i wrote which is not working for me that is
insert into calendar_date2
with dates as (
select dates(to_date('01.01.2020 07:45', 'dd.mm.yyyy hh24:mi'))+level dt
from dual
select start_date + round(dbms_random.value(1, 15)) / (24 * 60) result
from test
connect by level <= 365
)
select dt,
case
when to_char(dt, 'fmday') in ('sunday')
then 'N' else 'Y'
end
from dates
;
i write this but it not worked for me please help struck here from past many hours
You can use:
insert into calendar_date2 (dt, is_workday )
SELECT dt,
DECODE( TRUNC( dt ) - TRUNC( dt, 'IW' ), 6, 'N', 'Y' )
FROM (
SELECT DATE '2020-01-01' + FLOOR( (LEVEL - 1) / 15 )
+ FLOOR(DBMS_RANDOM.VALUE(1, 16)) * INTERVAL '1' MINUTE
AS dt
FROM DUAL
CONNECT BY
DATE '2020-01-01' + FLOOR( (LEVEL - 1) / 15 )
< ADD_MONTHS( DATE '2020-01-01', 12 )
)
Which for the table:
dt DATE
NOT NULL,
is_workday CHAR(1)
NOT NULL
CONSTRAINT calendar_date2__iwd__chk CHECK ( is_workday IN ( 'Y', 'N' ) )
);
Gives you 15 rows for each day of 2020 each with random offset between 1 and 15 minutes from midnight.
Note:
The number of days in the year is calculated so you do not need to account for leap years.
Use FLOOR( DBMS_RANDOM.VALUE( 1, 16 ) ) rather than ROUND( DBMS_RANDOM.VALUE( 1, 15 ) ) as the first will give you an even spread of values whereas the second will only give a value 1 when the random value is between 1 and 1.5 and a value of 15 between 14.5 and 15 which will both occur with only half the frequency as the rest of the numbers and would give you an unbalanced distribution.
You can calculate the day of the week by comparing the start of the day TRUNC( dt ) to the start of the ISO week TRUNC( dt, 'IW' ). This has the benefit that it is independent of the NLS settings and will work in all languages rather than just being limited to English speaking territories.
db<>fiddle here

Oracle SQL - How to retrieve the ID Count difference between today vs yesterday

I have a table that captures when a customer purchases a product. It captures a unique purchase id along with a timestamp of when the purchase was made.
I want to be able to query, the difference between how many purchases were taken today vs yesterday?
Not sure how to query this on oracle?
You can use conditional aggregation:
select sum(case when trunc(datecol) = trunc(sysdate - 1) then 1 else 0 end) as num_yesterday,
sum(case when trunc(datecol) = trunc(sysdate) then 1 else 0 end) as num_today,
sum(case when trunc(datecol) = trunc(sysdate) then 1
when trunc(datecol) = trunc(sysdate - 1) then -1
end) as diff
from t
where datecol >= trunc(sysdate - 1);
you can use the Group function to grouping the purchase day with timestamp information and count the purchase id.
select trunc(purchase_ts) Day, count(purchase_id) Count
from purchase
group by trunc(purchase_ts)
order by 1
Using TRUNC on the column will prevent Oracle from using an index on that column (instead you would need a separate function-based index); instead use a CASE statement to test whether the date is between the start of the day and the start of the next day and then COUNT the values between those ranges:
SELECT COUNT(
CASE
WHEN TRUNC( SYSDATE ) - INTERVAL '1' DAY <= your_date_column
AND your_date_coumn < TRUNC( SYSDATE )
THEN 1
END
) AS count_for_yesterday,
COUNT(
CASE
WHEN TRUNC( SYSDATE ) <= your_date_column
AND your_date_coumn < TRUNC( SYSDATE ) + INTERVAL '1' DAY
THEN 1
END
) AS count_for_today
FROM your_table
WHERE TRUNC( SYSDATE ) - INTERVAL '1' DAY <= your_date_column
AND your_date_coumn < TRUNC( SYSDATE ) + INTERVAL '1' DAY

Current status based on week number ORACLE

I got this query on order to get all the days from the first day of the year (01/01/2018) to the end of next year (31/12/2019).
SELECT MYDATE,
TO_CHAR(NR_OF_SUNDAYS + 1,'FM09') WEEK_NUM,
FROM
(
SELECT MYDATE,
( (TRUNC(MYDATE,'DAY') - TRUNC(TRUNC(MYDATE,'YYYY'),'DAY')) / 7 ) +
CASE WHEN TO_CHAR(TRUNC(MYDATE,'YYYY'),'DAY') = 'SUN' THEN 1 ELSE 0 END AS NR_OF_SUNDAYS
FROM
( SELECT TRUNC (SYSDATE, 'YY') - 1 + LEVEL AS MYDATE
FROM DUAL
CONNECT BY LEVEL <= TRUNC (ADD_MONTHS (SYSDATE, 24), 'YY') -
TRUNC (SYSDATE, 'YY')
)
)
I need a column that specifies the following cases:
1) CASE WHEN MYDATE < TO_CHAR(SYSDATE, 'DD/MM/YYYY') THEN 'PAST DUE'
(this works its easy and no problem)
2) if my current =< mydate
week_num then 'CURRENT WEEK'(Excluding PAST DUE)
3) if my current week + one week then
'NEXT WEEK' (Excluding PAST DUE)
4) else FUTURE
Thanks a lot for your help.
So, in my answer I tried retain the logic behind your week number calculation.
However keep in mind that you could calculate week number using oracle to_char(date,'WW'), to_char(date,'IW'), to_char(date,'W') functions and then your life would be easier.
WW Week of year (1-53) where week 1 starts on the first day of the year and continues to the seventh day of the year.
W Week of month (1-5) where week 1 starts on the first day of the month and ends on the seventh.
IW Week of year (1-52 or 1-53) based on the ISO standard.
Having said all that here is my solution that uses only sql (note that defining and using a function would be a lot easier), based on your calculation method.
with date_table as (
SELECT MYDATE, to_number(TO_CHAR(NR_OF_SUNDAYS + 1,'FM09')) WEEK_NUM, to_number(to_char(MYDATE+1,'IW')) as nu
FROM
(
SELECT MYDATE,
( (TRUNC(MYDATE,'DAY') - TRUNC(TRUNC(MYDATE,'YYYY'),'DAY')) / 7 ) +
CASE WHEN TO_CHAR(TRUNC(MYDATE,'YYYY'),'DY', 'NLS_DATE_LANGUAGE = american') = 'SUN' THEN 1 ELSE 0 END AS NR_OF_SUNDAYS
FROM
( SELECT TRUNC (SYSDATE, 'YY') - 1 + LEVEL AS MYDATE
FROM DUAL
CONNECT BY LEVEL <= TRUNC (ADD_MONTHS (SYSDATE, 24), 'YY') -TRUNC (SYSDATE,'YY')
)
)
),
todays_week as
(
select distinct WEEK_NUM from date_table
where trunc(sysdate)=trunc(mydate)
),
pre_final as (
select MYDATE,WEEK_NUM, (select WEEK_NUM from todays_week) as todaysweek from date_table)
select MYDATE,sysdate,WEEK_NUM,todaysweek,
case when trunc(MYDATE) < trunc(sysdate) then 'PAST DUE'
when todaysweek = WEEK_NUM and abs(MYDATE-sysdate)<=7 then 'CURRENT WEEK'
when todaysweek +1 = WEEK_NUM and abs(MYDATE-sysdate)<=14 then 'Next Week'
else 'Future' end as description
from pre_final;
The main idea is to find today's week number and then use case when.
Here is my fiddle link with the results.
http://sqlfiddle.com/#!4/3149e4/148
EDIT 1:
Now, similar results one could achive with something like this:
select res.*,
case when trunc(MYDATE) < trunc(sysdate) then 'PAST DUE'
when todaysweek = WEEK_NUM and abs(MYDATE-sysdate)<=7 then 'CURRENT WEEK'
when todaysweek +1 = WEEK_NUM and abs(MYDATE-sysdate)<=14 then 'Next Week'
else 'Future' end as description
from (
SELECT MYDATE, to_number(to_char(MYDATE,'IW')) as WEEK_NUM,to_number(to_char(sysdate,'IW')) as todaysweek
FROM
( SELECT TRUNC (SYSDATE, 'YY') - 1 + LEVEL AS MYDATE
FROM DUAL
CONNECT BY LEVEL <= TRUNC (ADD_MONTHS (SYSDATE, 24), 'YY') -TRUNC (SYSDATE,'YY')
)) res

Get data for last 12 weeks oracle

Hello i need to get transaction count and total amount for transactions in last quarter.
i use following to get data for a quarter (last 90 days)
WITH date_range as
(SELECT TRUNC(sysdate) - 90 + level AS week_day
FROM dual
CONNECT BY ROWNUM <= 90),
the_data
AS (SELECT TRUNC(systemdate) AS log_date, count(*) AS num_obj,status AS log_status, nvl(sum(
CASE
WHEN VERSION = '1.1'
THEN nvl(amount/100,'0.0')
ELSE nvl(amount,'0.0')
END), 0) AS totalamount
from transactionlog where ((merchantcode in (
SELECT regexp_substr('MERC0003','[^,]+', 1, LEVEL) FROM dual
connect by regexp_substr('MERC0003', '[^,]+', 1, level) is not null ) OR 'MERC0003' IS NULL) AND status = 'xxxx')
GROUP BY TRUNC(systemdate),status)
SELECT TO_CHAR(dr.week_day,'DD/MM/YYYY HH:MI AM') AS TXNDATE, NVL(trans_log.num_obj,0) as TXNCOUNT,trans_log.log_status,trans_log.totalamount
FROM date_range dr LEFT OUTER JOIN the_data trans_log
on trans_log.log_date = dr.week_day
ORDER BY dr.week_day DESC ;
From above, I get 90 days record , as in 90 rows containing transaction count and amount for 90 days,
i need to get data in terms of weeks in quarter. That is, 12 rows containing data for each week which has transaction count and amount in last 12 weeks.
You can use TRUNC( date_value ,'IW' ) to truncate a date to the start of the ISO week (Monday 00:00) and add multiples of a week in your date range and perform the same truncation in your query:
WITH date_range( week_day ) as (
SELECT TRUNC( sysdate - 90, 'IW' ) + ( level - 1 ) * INTERVAL '7' DAY
FROM dual
CONNECT BY TRUNC( sysdate - 90, 'IW' ) + ( level - 1 ) * INTERVAL '7' DAY <= SYSDATE
),
the_data ( log_date, num_obj, log_status, totalamount ) AS (
SELECT TRUNC(systemdate, 'IW'),
-- rest of your query
GROUP BY TRUNC(systemdate, 'IW'),
status
)
SELECT *
FROM date_range dr
LEFT OUTER JOIN the_data trans_log
on trans_log.log_date = dr.week_day
ORDER BY dr.week_day DESC;

Using variables in Oracle SQL

I'm not much experienced with Oracle SQL and been trying to accomplish some task for which I intend to use
select count(*) from TABLE where date = (select trunc(sysdate -1 ) from dual)
The above query gives me the result for all the records with sysdate minus 1. I want to automate it to handle the scenario of Sat and Sunday and modify the query to something like below
select count(*) from TABLE where date = (select trunc(sysdate -#var ) from dual)
where #var = 2 if the query runs on Sunday and #var = 3 if the query is running on Monday
TRUNC( SYSDATE, 'IW' ) will truncate the date to the start of the ISO week - so midnight Monday. TRUNC( SYSDATE ) - TRUNC( SYSDATE, 'IW' ) will give the number of full days since the start of the ISO week (Monday = 0, Tuesday = 1, ... Sunday = 6) and you can just put that into a CASE statement:
select count(*)
from your_table
where date_col = TRUNC(SYSDATE) - CASE TRUNC(SYSDATE) - TRUNC(SYSDATE, 'IW')
WHEN 0 THEN 3 -- Monday
WHEN 6 THEN 2 -- Sunday
ELSE 1 -- Other days
END