Count the no of saturdays and sundays in date range - oracle [duplicate] - oracle

This question already has answers here:
Number of fridays between two dates
(7 answers)
Closed 8 years ago.
I have two parameters(start_Date,end_Date) from table1
I'm trying to count no of saturdays and sundays in a date range
star_Date=8/20/2014 13:52
end_Date=8/28/2014 13:52
And result should be like this
Start_Date end_date No_of_leaves
8/20/2014 13:52 8/28/2014 13:52 2
Update Section
SELECT retouch_req_time,retouch_submit_time,(
SELECT Count(*) FROM (SELECT To_char(start_date + ( LEVEL - 1 ), 'fmday') dt
FROM (WITH t AS (SELECT To_date (retouch_req_time, 'MM/DD/YYYY HH24:MI') start_date, To_date (retouch_submit_time, 'MM/DD/YYYY HH24:MI') end_date FROM TT))
CONNECT BY LEVEL <= end_date - start_date + 1) WHERE dt IN ('friday','saturday')) as worked_hours
FROM TT

You can try using hierarchical queries
WITH t
AS (SELECT To_date ('8/20/2014 13:52', 'MM/DD/YYYY HH24:MI') start_date,
To_date ('8/28/2014 13:52', 'MM/DD/YYYY HH24:MI') end_date
FROM dual)
SELECT Count(*)
FROM (SELECT To_char(start_date + ( LEVEL - 1 ), 'fmday') dt
FROM t
CONNECT BY LEVEL <= end_date - start_date + 1)
WHERE dt IN ( 'friday', 'saturday' );
RESULT
------
2
* The dates are listed by expanding the range.
* The TO_CHAR function is used to obtain the weekday
* Count everthing which is a friday or saturday
If you want to find the day wise count, then you can try
SELECT To_char(dat, 'DY'),
Count(*)
FROM (SELECT To_date ('8/20/2014 13:52', 'MM/DD/YYYY HH24:MI')
+ num dat
FROM (SELECT LEVEL - 1 num
FROM dual
CONNECT BY LEVEL <= Abs(To_date ('8/20/2014 13:52',
'MM/DD/YYYY HH24:MI') -
To_date (
'8/28/2014 13:52'
,
'MM/DD/YYYY HH24:MI')) - 1
))
WHERE To_char(dat, 'DY') IN ( 'FRI', 'SAT' )
GROUP BY To_char(dat, 'DY');
RESULTS
TO_CHAR(DAT,'DY') COUNT(*)
----------------- --------
FRI 1
SAT 1

You can calculate the number of saturdays and sundays like this:
with t(d) as (
select sysdate + level from dual connect by rownum < 10
)
select count(case when trim(to_char(d, 'DAY')) in ('SATURDAY', 'SUNDAY') then 1 end) cnt from t
CNT
---
2
If you don't have a range of dates then:
with t(a, b) as (
select sysdate a, sysdate + 10 b from dual connect by rownum < 10
), t2(d) as (
select a + level - 1 from t connect by rownum <= b - a
)
select count(case when trim(to_char(d, 'DAY')) in ('SATURDAY', 'SUNDAY') then 1 end) cnt from t2
CNT
---
2

Related

How to give a client's state by time

Table t_customer_statistics
trx_date - transaction date
cuid - id person(divide prospect and client)
lifecycle_status - this column must be filled
I need to give status to a client based on his condition
acquired - this month was the very first transaction
existing - there was a transaction last month
reactivated - there was no transaction last month
sleeping - there has been no transaction for the last 90 days (there have been no subsequent ones since the last transaction, more than 90 days)
I roughly made a code like this
UPDATE t_customer_statistics
SET Lifecycle_status =
case
when to_char (trunc (trx_date, 'mm'), 'mm.yyyy') = to_char (trunc (sysdate, 'mm'), 'mm.yyyy') then 'acquired'
when to_char (trunc (trx_date, 'mm'), 'mm.yyyy') = to_char (trunc (sysdate, 'mm') - 1, 'mm.yyyy') then 'existing'
when to_char (trunc (trx_date, 'mm'), 'mm.yyyy') = to_char (trunc (sysdate, 'mm') - 40, 'mm.yyyy') then 'reactivated'
when to_char (trunc (trx_date, 'mm'), 'mm.yyyy') <to_char (trunc (sysdate, 'mm') - 90, 'mm.yyyy') then 'sleeping'
end
but when they gave me an example, if the client made the first transaction and then fell asleep, then he has two states in the end, and sleeping must be separated so that there is a separate
PS. must be considered by transaction from the first and last
You could use a MERGE statement like this:
MERGE INTO clients dst
USING (
SELECT rowid rid,
LEAD(dt, 1) OVER (PARTITION BY id ORDER BY dt DESC) AS prev_dt,
LAG(dt, 1) OVER (PARTITION BY id ORDER BY dt DESC) AS next_dt
FROM clients
) src
ON ( dst.ROWID = src.rid )
WHEN MATCHED THEN
UPDATE
SET status = CASE
WHEN prev_dt IS NULL
THEN 'acquired'
WHEN MONTHS_BETWEEN(TRUNC(dst.dt, 'MM'), TRUNC(src.prev_dt)) <= 1
THEN 'existing'
ELSE 'reactivated'
END
||
CASE
WHEN COALESCE(src.next_dt, SYSDATE) >= dst.dt + INTERVAL '90' DAY
THEN ', sleeping'
END;
Which, for the sample data:
CREATE TABLE clients (id, dt, status ) AS
SELECT 1, DATE '2020-01-01', CAST( NULL AS VARCHAR2(20) ) FROM DUAL UNION ALL
SELECT 1, DATE '2020-02-01', CAST( NULL AS VARCHAR2(20) ) FROM DUAL UNION ALL
SELECT 1, DATE '2020-03-01', CAST( NULL AS VARCHAR2(20) ) FROM DUAL UNION ALL
SELECT 1, DATE '2020-05-01', CAST( NULL AS VARCHAR2(20) ) FROM DUAL UNION ALL
SELECT 1, DATE '2020-09-01', CAST( NULL AS VARCHAR2(20) ) FROM DUAL UNION ALL
SELECT 1, DATE '2020-10-01', CAST( NULL AS VARCHAR2(20) ) FROM DUAL UNION ALL
SELECT 1, DATE '2020-10-01' + INTERVAL '91' DAY, CAST( NULL AS VARCHAR2(20) ) FROM DUAL;
Then the result of the MERGE is:
ID
DT
STATUS
1
01-JAN-20
acquired
1
01-FEB-20
existing
1
01-MAR-20
existing
1
01-MAY-20
reactivated, sleeping
1
01-SEP-20
reactivated
1
01-OCT-20
existing, sleeping
1
31-DEC-20
reactivated, sleeping
db<>fiddle here

calculate the running total over the column contain date difference in HH:MI:SS format in oracle

I have to find the running total over the column interval.
SELECT
( ( EXTRACT(DAY FROM intrvl) * 24 ) + ( EXTRACT(HOUR FROM intrvl) ) ) ||':'||
EXTRACT(MINUTE FROM intrvl) ||':'||
EXTRACT(SECOND FROM intrvl) ||':'|| as interval
FROM
(
SELECT
( to_timestamp(TO_CHAR(date_column_name,'dd-mon-rrrr hh:mi:ss') ) ) - ( to_timestamp(TO_CHAR(date_column_name,'dd-mon-rrrr hh:mi:ss') ) ) intrvl
FROM
dual
);
currrently Interval column of table has below data:
Interval(HH:mi:ss)
0:4:23
696:1:36
696:4:51
8760:1:18
The best I can come up with is this. Note that the interval data type does not take a format model for displaying - you can't force an interval of 25 hours to be displayed as 25:00:00 (although you can use that to INPUT an interval). Instead, it will be shown as 01 01:00:00 (meaning, a day and an hour).
with
tbl (interv) as (
select interval '0:4:23' hour(9) to second from dual union all
select interval '696:1:36' hour(9) to second from dual union all
select interval '696:4:51' hour(9) to second from dual union all
select interval '8760:1:18' hour(9) to second from dual
)
select interval '1' day * sum(date '2000-01-01' + interv - date'2000-01-01')
as sum_interv
from tbl;
SUM_INTERV
--------------------
+423 00:12:08.000000
In your original attempt you were trying to get a STRING output. I am not sure that's wise, but if that's what you need you can do it like so:
with
tbl (interv) as (
select interval '0:4:23' hour(9) to second from dual union all
select interval '696:1:36' hour(9) to second from dual union all
select interval '696:4:51' hour(9) to second from dual union all
select interval '8760:1:18' hour(9) to second from dual
)
, prep (sum_interv) as (
select interval '1' day * sum(date '2000-01-01' + interv - date'2000-01-01')
from tbl
)
select to_char( extract(day from sum_interv) * 24
+ extract(hour from sum_interv), 'fm999999999' ) || ':' ||
to_char( extract(minute from sum_interv), 'fm00' ) || ':' ||
to_char( extract(second from sum_interv), 'fm00' ) as sum_interv
from prep
;
SUM_INTERV
------------------
10152:12:08

Oracle: Days between two date and Exclude weekdays how to handle negative numbers

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
;

How to query for a specific day of the month in Oracle

Trying to automate a query that will pull data for the current month where the day of the month (in the date field) is >= the 15th. Is this possible? If so, what is the syntax to achieve this?
I want to be able to run this query each month without having to change anything. So in May, it would automatically pull any item where the date was >= 5/15/16. In June, it would pull items where the date was >= 6/15/16. And so on.....
Any help in this would be greatly appreciated. Thanks
This will allow you to use any indexes you have on your date_field column:
SELECT *
FROM table_name
WHERE date_field >= TRUNC( SYSDATE, 'MM' ) + INTERVAL '14' DAY
AND date_field < ADD_MONTHS( TRUNC( SYSDATE, 'MM' ), 1 );
If your date/time fields are of type timestamp you can do
select x from <your_table>
where EXTRACT(DAY from <timestamp field>) >=15
and EXTRACT(MONTH from <timestamp field>) = EXTRACT(MONTH FROM CURRENT_TIMESTAMP)
and EXTRACT(YEAR from <timestamp field>) = EXTRACT(YEAR FROM CURRENT_TIMESTAMP);
I think what you're after is something like:
with sample_data as (select 1 id, to_date('01/06/2016', 'dd/mm/yyyy') dt from dual union all
select 2 id, to_date('10/06/2016', 'dd/mm/yyyy') dt from dual union all
select 3 id, to_date('14/06/2016', 'dd/mm/yyyy') dt from dual union all
select 4 id, to_date('15/06/2016', 'dd/mm/yyyy') dt from dual union all
select 5 id, to_date('16/06/2016', 'dd/mm/yyyy') dt from dual union all
select 6 id, to_date('30/06/2016', 'dd/mm/yyyy') dt from dual union all
select 7 id, to_date('01/07/2016', 'dd/mm/yyyy') dt from dual)
select *
from sample_data
where dt >= trunc(sysdate, 'mm') + 14
and dt < last_day(trunc(sysdate)) + 1;
ID DT
---------- ----------
4 15/06/2016
5 16/06/2016
6 30/06/2016
(If you wanted rows with any date greater than the 15th of the current month, then remove the last predicate in the where clause.)

How to get a list of months between 2 given dates using a query?

I have 2 dates, say 28-Mar-2011 and 29-Jun-2011. I need an sql query that will display the months between these 2 dates including the months containing the dates, ie. June, May, April and March.
Something like this
SQL> ed
Wrote file afiedt.buf
select to_char( add_months( start_date, level-1 ), 'fmMonth' )
from (select date '2011-03-30' start_date,
date '2011-06-29' end_date
from dual)
connect by level <= months_between(
trunc(end_date,'MM'),
trunc(start_date,'MM') )
* + 1
SQL> /
TO_CHAR(ADD_MONTHS(START_DATE,LEVEL-
------------------------------------
March
April
May
June
should work.
Gonna add this solution just because I think it's much cleaner than the others:
SELECT ADD_MONTHS(TRUNC(TO_DATE('28-Mar-2011', 'DD-MON-YYYY'), 'MON'), ROWNUM - 1) date_out
FROM DUAL
CONNECT BY ADD_MONTHS(TRUNC(TO_DATE('28-Mar-2011', 'DD-MON-YYYY'), 'MON'), ROWNUM - 1)
<= TRUNC(TO_DATE('29-Jun-2011', 'DD-MON-YYYY'), 'MON')
You can use the function MONTHS_BETWEEN
SELECT MOD( TRUNC( MONTHS_BETWEEN( '2011-07-29', '2011-03-28' ) ), 12 ) as MONTHS
FROM DUAL
Output
MONTHS
----------
4
I needed an answer to this a couple of days ago. I found another solution I liked more:
select to_char(which_month, 'Mon-yyyy') month
from
(
select
add_months(to_date(:start_date,'mm-yyyy'), rownum-1) which_month
from
all_objects
where
rownum <= months_between(to_date(:end_date,'mm-yyyy'), add_months(to_date(:start_date,'mm-yyyy'), -1))
order by
which_month
)
You could of course use any format you want. I 'union'ed and summed over another set so that I'd get the months even when they didn't have results.
SELECT MIN (to_date((TO_CHAR (Actual_Date, 'DD-MM-RRRR')),'dd-mm-rrrr')) F_DATE,
MAX (to_date((TO_CHAR (Actual_Date, 'DD-MM-RRRR')),'dd-mm-rrrr')) T_DATE,
TO_CHAR (Actual_Date, 'MM-RRRR') TRX_MONTH
FROM ( SELECT TRUNC (TO_DATE (:P_FDATE, 'dd-mm-rrrr')) + LEVEL - 1
Actual_Date
FROM (SELECT TRUNC (TO_DATE (:P_FDATE, 'dd-mm-rrrr'), 'MM') - 1
AS dt
FROM DUAL)
CONNECT BY LEVEL <=
( TO_DATE (:P_TDATE, 'dd-mm-rrrr')
- TRUNC (TO_DATE (:P_FDATE, 'dd-mm-rrrr'))
+ 1))
GROUP BY TO_CHAR (Actual_Date, 'MM-RRRR')
ORDER BY 1
declare
v_date_from_first_day date;
v_date_to_last_day date;
v_month_name varchar2(10);
v_month_number number;
v_year_number number;
v_month_diff number;
begin
v_date_to_last_day := to_date('31.12.2018');
v_date_from_first_day := to_date('01.01.2018');
select months_between(v_date_to_last_day,v_date_from_first_day) as diff into v_month_diff from dual;
for i in 1..round(v_month_diff, 2) loop
select
to_char(trunc(add_months(v_date_to_last_day - months_between(v_date_from_first_day, v_date_to_last_day), -i)), 'fmMonth') as month_nm,
to_char(trunc(add_months(v_date_to_last_day - months_between(v_date_from_first_day, v_date_to_last_day), -i)), 'MM') as month_num,
to_char(trunc(add_months(v_date_to_last_day - months_between(v_date_from_first_day, v_date_to_last_day), -i)), 'YYYY') as year_num
into v_month_name, v_month_number, v_year_number
from dual;
dbms_output.put_line(v_month_number || '/' || v_year_number);
dbms_output.put_line(v_month_name || '/' || v_year_number);
end loop;
end;
Output:
12/2018
11/2018
10/2018
9/2018
8/2018
7/2018
6/2018
5/2018
4/2018
3/2018
2/2018
1/2018
Here, month names are in Croatian
Prosinac/2018
Studeni/2018
Listopad/2018
Rujan/2018
Kolovoz/2018
Srpanj/2018
Lipanj/2018
Svibanj/2018
Travanj/2018
Ožujak/2018
Veljača/2018
Siječanj/2018

Resources