calculate number of saturdays+sunday total count in oracle - oracle

i have a query and I want to calculate the number of sat+sun total count in oracle, for example, I have a query pasted below there should be a total count of Saturday and Sunday, how can I achieve that please help, I really appreciate any help you can provide.
SELECT TO_DATE('01-12-2022','dd-mm-yyyy') start_date , TO_DATE(sysdate) end_date
FROM dual;

Don't use a row-generator to create a calendar (as it is very inefficient); just calculate the number by calculating the number of full weeks and then deal with the part weeks at the start and end of the range:
WITH range (start_date, end_date) AS (
SELECT DATE '2022-12-01', TRUNC(SYSDATE) FROM DUAL
)
SELECT -- Number of full weeks
(TRUNC(end_date, 'IW') - TRUNC(start_date, 'IW')) * 2/7
-- Number of weekend days in final week
+ GREATEST(end_date - TRUNC(end_date, 'IW') - 4, 0)
-- Number of weekend days in before first week
- GREATEST(start_date - TRUNC(start_date, 'IW') - 5, 0)
AS weekend_day_count
FROM range;
Which outputs:
WEEKEND_DAY_COUNT
8
fiddle

One option is to create a calendar between these two dates and then count number of Saturdays and Sundays:
SQL> with
2 test (start_date, end_date) as
3 -- period
4 (select date '2022-12-01', date '2022-12-29' from dual),
5 calendar as
6 -- calendar (all dates between START_DATE and END_DATE)
7 (select start_date + level - 1 as datum
8 from test
9 connect by level <= end_date - start_date + 1
10 )
11 -- number of Saturdays and Sundays
12 select count(*)
13 from calendar
14 where to_char(datum, 'dy', 'nls_date_language = english') in ('sat', 'sun');
COUNT(*)
----------
8
SQL>
You'd change dates at line #4.
P.S. If you look at code MT0 posted and their objection that row generator is inefficient, that's true. Although both queries return the same result, timing is different. For example:
Period 01.01.2022 - 31.12.2022 01.01.1900 - 31.12.2022 01.01.0001 - 31.12.2022
------ ----------------------- ----------------------- -----------------------
LF 00:00:00.00 00:00:00.17 00:00:03.72
MT0 00:00:00.02 00:00:00.02 00:00:00.05
It is obvious that my timing gets worse with period length. If you're looking at one year or a century, the difference is mostly irrelevant. For 2000 years, the difference is huge!
However, if you consider debugging, from my own point of view, my code is easier to read: "select number of rows from the calendar where date is either saturday or sunday" - plain English.
On the other hand, the other code isn't that straightforward; truncate date to week, subtract them, multiply by 2/7 (why "2/7" and not 4/9?), add result returned by the GREATEST function minus 4 (why 4? Why not 7?), subtract GREATEST of something minus 5 (why 5? Why not 2?) - as I said, that's NOT easy to read nor understand.
Therefore, it depends on what you actually need, timing vs. readability. Pick one :)

Related

Where clause from a subquery

I have a table with business days BUSINESS_DAYS which has all the dates
I have another table with payment information and DUE_DATES
I want to return in my query the next business day IF the DUE_DATE is not a business day
SELECT SQ1.DUE_DATE, SQ2.DATE FROM
(SELECT * FROM
PAYMENTS
ORDER BY
DUE_DATE) SQ1,
(SELECT MIN(DATE) DATE FROM BUSINESS_DAYS WHERE SQ1.DUE_DATE <= DATE GROUP BY DATE) SQ2
Anyone can shed some light?
The way I see it, code you posted doesn't do what you wanted anyway (otherwise, you won't be asking a question at all). Therefore, I'd suggest another approach:
Altering the session (you don't have to do it; my database speaks Croatian so I'm switching to English; also, setting date format to display day name):
SQL> alter session set nls_date_language = 'english';
Session altered.
SQL> alter session set nls_date_format = 'dd.mm.yyyy, dy';
Session altered.
Two CTEs contain
business_days: as commented, only this year's July, weekends excluded, there are no holidays)
payments: two rows, one whose due date is a working day and another whose isn't
Sample data end at line #15, query you might be interested in begins at line #16. Its CASE expression check whether due_date is one of weekend days; if not, due date to be returned is exactly what it is. Otherwise, another SELECT statement returns the first (MIN) business day larger than due_date.
SQL> with
2 business_days (datum) as
3 -- for simplicity, only all dates in this year's July,
4 -- weekends excluded (as they aren't business days), no holidays
5 (select date '2021-07-01' + level - 1
6 from dual
7 where to_char(date '2021-07-01' + level - 1, 'dy')
8 not in ('sat', 'sun')
9 connect by level <= 31
10 ),
11 payments (id, due_date) as
12 (select 1, date '2021-07-14' from dual -- Wednesday, business day
13 union all
14 select 2, date '2021-07-25' from dual -- Sunday, non-business day
15 )
16 select p.id,
17 p.due_date current_due_date,
18 --
19 case when to_char(p.due_date, 'dy') not in ('sat', 'sun') then
20 p.due_date
21 else (select min(b.datum)
22 from business_days b
23 where b.datum > p.due_date
24 )
25 end new_due_date
26 from payments p
27 order by id;
ID CURRENT_DUE_DAT NEW_DUE_DATE
---------- --------------- ---------------
1 14.07.2021, wed 14.07.2021, wed --> Wednesday remains "as is"
2 25.07.2021, sun 26.07.2021, mon --> Sunday switched to Monday
SQL>

Use EPOCH time for timestamp to get records within 1 minute

I was curious to see how in Oracle 12c you can take a timestamp datatype and convert the records into EPOCH time to make them a number and then use that number to find any records within that date column that are within 1 minute of each other (assuming the same day if needed, or simply any calculations within 1 minute).
I tried the following but got an ORA-01873: the leading precision of the interval is too small error.
select (sold_date - to_date('1970-01-01 00:00:00','YYYY-MM-DD HH24:MI:SS'))*86400 as epoch_sold_date from test1;
What is SOLD_DATE? For e.g. SYSDATE (function that returns DATE datatype), your code works OK.
SQL> select (sysdate
2 - to_date('1970-01-01 00:00:00','YYYY-MM-DD HH24:MI:SS')
3 ) * 86400 as epoch_sold_date
4 from dual;
EPOCH_SOLD_DATE
---------------
1600807918
SQL>
As SOLD_DATE is a timestamp, but - it appears that fractions of a second aren't or special interest to you, cast it to DATE:
select (cast (systimestamp as date) --> this
- to_date('1970-01-01 00:00:00','YYYY-MM-DD HH24:MI:SS')
) * 86400 as epoch_sold_date
from dual;
Saying that you get the same result for all rows: well, I don't, and you shouldn't either if SOLD_DATE differs.
SQL> with test (sold_date) as
2 (select timestamp '2020-09-22 00:00:00.000000' from dual union all
3 select timestamp '2015-03-18 00:00:00.000000' from dual
4 )
5 select sold_date,
6 (cast (sold_date as date)
7 - to_date('1970-01-01 00:00:00','YYYY-MM-DD HH24:MI:SS')
8 ) * 86400 as epoch_sold_date
9 from test;
SOLD_DATE EPOCH_SOLD_DATE
------------------------------ ---------------
22.09.20 00:00:00,000000000 1600732800
18.03.15 00:00:00,000000000 1426636800
SQL>
One more edit: when you subtract two timestamps, result is interval day to second. If you extract minutes from it, you get what you wanted:
SQL> with test (sold_date) as
2 (select timestamp '2020-09-22 10:15:00.000000' from dual union all
3 select timestamp '2015-03-18 08:05:00.000000' from dual
4 )
5 select sold_date,
6 lead(sold_date) over (order by sold_date) next_sold_date,
7 --
8 lead(sold_date) over (order by sold_date) - sold_date diff,
9 --
10 extract(day from lead(sold_date) over (order by sold_date) - sold_date) diff_mins
11 from test
12 order by sold_date;
SOLD_DATE NEXT_SOLD_DATE DIFF DIFF_MINS
------------------------------ ------------------------------ ------------------------------ ----------
18.03.15 08:05:00,000000000 22.09.20 10:15:00,000000000 +000002015 02:10:00.000000000 2015
22.09.20 10:15:00,000000000
SQL>
In your case, you'd check whether extracted minutes value is larger than 1 (minute).
If you just want to see how many minutes are there between two timestamps, then
cast them to dates
subtract those dates (and you'll get number of days)
multiply it by 24 (as there are 24 hours in a day) and by 60 (as there are 60 minutes in an hour)
Something like this:
SQL> with test (date_1, date_2) as
2 (select timestamp '2020-09-22 10:15:00.000000',
3 timestamp '2020-09-22 08:05:00.000000' from dual
4 )
5 select (cast(date_1 as date) - cast(date_2 as date)) * 24 * 60 diff_minutes
6 from test;
DIFF_MINUTES
------------
130
SQL>
If you are just looking to compare dates and find rows that are within one minute of each other, you do not need to use epoch time. There are several solutions to this problem on this thread.

Selecting every 3 seconds data rows between 2 dates

I have a requirement to take every 3 seconds data within the specific time interval in SQL. I am new to SQL so can anyone help me on the scenario
This is my select query which returns all the values but i need data for every 3 seconds only
SELECT ton_nbr
FROM
icr_file_interface
WHERE
(
reading_dttm BETWEEN
TO_DATE(concat('2016-10-19',to_char(0930)),'yyyy-mm-dd HH24MISS')
AND TO_DATE(concat('2016-10-19',to_char('0945')),'yyyy-mm-dd HH24MISS')
)
AND
(
ton_nbr BETWEEN
(SELECT value FROM text_para WHERE para_cd='ICR_ST_RNG')
AND (SELECT value FROM text_para WHERE para_cd='ICR_ED_RNG')
)
If you only need to subtract 3 seconds from a date, you can use the following:
SQL> select to_char(sysdate, 'yyyy-mm-dd hh24:mi:ss'),
2 to_char(sysdate - 3*1/24/60/60, 'yyyy-mm-dd hh24:mi:ss')
3 from dual;
TO_CHAR(SYSDATE,'YY TO_CHAR(SYSDATE-3*1
------------------- -------------------
2016-10-19 09:38:17 2016-10-19 09:38:14
Given that sysdate -1 means "subtract one day to sysdate", you can derive the number of seconds you need with a bit af arithmetic
This selects you data between last 3 seconds. Hope you got the idea.
select ton_nbr
from icr_file_interface
where reading_dttm between dateadd(ss, -3, getdate()) and getdate() ;

Oracle Date - How to add years to date

I have a date field
DATE = 10/10/2010
sum = 4 (this are number of years by calculation)
is there a way to add four years to 10/10/2010 and make it
10/10/2014?
Try adding months (12 * number of years) instead. Like this-
add_months(date'2010-10-10', 48)
Use add_months
Example:
SELECT add_months( to_date('10-OCT-2010'), 48 ) FROM DUAL;
Warning
add_months, returns the last day of the resulting month if you input the last day of a month to begin with.
So add_months(to_date('28-feb-2011'),12) will return 29-feb-2012 as a result.
I believe you could use the ADD_MONTHS() function. 4 years is 48 months, so:
add_months(DATE,48)
Here is some information on using the function:
http://www.techonthenet.com/oracle/functions/add_months.php
http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:1157035034361
You can try this:
someDate + interval '4' year
INTERVAL
I am not sure, if I understood Your question correctly, but
select add_months(someDate, numberOfYears * 12) from dual
might do the trick
One more option apart from ADD_MONTHS
SELECT
SYSDATE,
SYSDATE
+ TO_YMINTERVAL ( '1-0' )
FROM
DUAL;
SYSDATE SYSDATE+TO_YMINTERVAL('1-0')
--------- ----------------------------
29-OCT-13 29-OCT-14
1 row selected.
SELECT
SYSDATE,
SYSDATE
+ TO_YMINTERVAL ( '2-0' )
FROM
DUAL;
SYSDATE SYSDATE+TO_YMINTERVAL('2-0')
--------- ----------------------------
29-OCT-13 29-OCT-15
1 row selected.
SELECT
TO_DATE ( '29-FEB-2004',
'DD-MON-YYYY' )
+ TO_YMINTERVAL ( '1-0' )
FROM
DUAL
*
Error at line 4
ORA-01839: date not valid for month specified
But the last one is illegal since there is no 29th day of February in 2005, hence it fails on leap year cases (Feb 29)
Read the documentation for the same
SELECT TO_CHAR(SYSDATE,'YYYY')-2 ANO FROM DUAL

How to get array using Oracle sql query

I want to display result set of Years between From date - To date using oracle SQL on dual table
e.g.
if i pass - From date as 1/1/1900 and To Date as 1/1/2000
then it shoold display
Only Years
1900
1901
1902
-
-
2000
There are two parts to this question. Generating the range of dates is quite simple: just use the trick with CONNECT BY that I demonstrated here.
edit
Generating a list of first of New Year's Days is quite simple:
SQL> select add_months(to_date('01-jan-1900'), (level-1)*12) as year
2 from dual
3 connect by level <= 101
4 /
YEAR
---------
01-JAN-00
01-JAN-01
01-JAN-02
...
01-JAN-98
01-JAN-99
01-JAN-00
101 rows selected.
SQL>
You just want the years? Well either use to_char(... , 'YYYY') on that. Or cut to the chase and just generate a list of numbers from 1900 - 2000.
The trickiest part of your request is getting the number of years. It would be easier to be given a start date and an offset, rather than an end date. Anyway ...
SQL> select to_char(add_months(to_date('&&start_date'), (level-1)*12), 'YYYY') as year
2 from dual
3 connect by level <= ( to_number(to_char(to_date('&&end_date'), 'yyyy'))
4 -to_number(to_char(to_date('&&start_date'), 'yyyy')) ) + 1
5 /
Enter value for start_date: 01-jan-1900
old 1: select add_months(to_date('&&start_date'), (level-1)*12) as year
new 1: select add_months(to_date('01-jan-1900'), (level-1)*12) as year
Enter value for end_date: 01-jan-2000
old 3: connect by level <= ( to_number(to_char(to_date('&&end_date'), 'yyyy'))
new 3: connect by level <= ( to_number(to_char(to_date('01-jan-2000'), 'yyyy'))
old 4: -to_number(to_char(to_date('&&start_date'), 'yyyy')) ) - 1
new 4: -to_number(to_char(to_date('01-jan-1900'), 'yyyy')) ) - 1
YEAR
----
1900
1901
1902
...
1998
1999
2000
101 rows selected.
SQL>

Resources