How to call a dynamic day in sql? - sql

I have a table I want to pull all date records before the most recent Friday. I know you can use sysdate (or getdate) to pull the current day, but all the solutions to similar questions I've looked at explicitly specify the numeric day of the week in the query. Todays Thursday so the below would work, but is there a dynamic alternative to this?
SELECT *
FROM table
WHERE datefield < sysdate - 6

You could use:
SELECT NEXT_DAY(TRUNC(SYSDATE) - 7, 'FRIDAY') AS last_friday
FROM DUAL;
However, if someone tries to query the data and is using a different language then you will get an error. I.e.:
ALTER SESSION SET NLS_DATE_LANGUAGE = 'FRENCH';
SELECT NEXT_DAY(TRUNC(SYSDATE) - 7, 'FRIDAY') AS last_friday
FROM DUAL;
Outputs:
ORA-01846: not a valid day of the week
A solution that works regardless of the language is to compare the day to the start of the ISO week (which is always a Monday):
SELECT TRUNC(SYSDATE, 'IW')
+ CASE WHEN SYSDATE - TRUNC(SYSDATE, 'IW') < 5
THEN -3
ELSE +4
END AS last_friday
FROM DUAL;
Outputs (with the NLS_DATE_FORMAT set to YYYY-MM-DD HH24:MI:SS (DY)):
LAST_FRIDAY
2022-01-14 00:00:00 (FRI)
db<>fiddle here
Your query would be:
SELECT *
FROM table
WHERE datefield < TRUNC(SYSDATE, 'IW')
+ CASE WHEN SYSDATE - TRUNC(SYSDATE, 'IW') < 5
THEN -3
ELSE +4
END

next_day function might help.
SQL> with test (datum) as
2 -- sample data; this January up to today
3 (select trunc(sysdate, 'mm') + level - 1
4 from dual
5 connect by level <= 20
6 )
7 select to_char(datum, 'dd.mm.yyyy, dy') datum
8 from test
9 where datum < next_day(sysdate - 7, 'FRIDAY')
10 order by datum;
DATUM
------------------------
01.01.2022, sat
02.01.2022, sun
03.01.2022, mon
04.01.2022, tue
05.01.2022, wed
06.01.2022, thu
07.01.2022, fri
08.01.2022, sat
09.01.2022, sun
10.01.2022, mon
11.01.2022, tue
12.01.2022, wed
13.01.2022, thu
14.01.2022, fri
14 rows selected.
SQL>

Related

How to get previous working date using trunc(sysdate) in oracle

Greetings for the day!
i have written a python script that will run a select query using oracle database and will share the result with users based on the result it gets from that query. My aim is to run it through task scheduler on which it should automatically adjust the date mentioned in sql query and should always pick the last business day means if its monday, it should run the query with asof day as friday, if tuesday then asod day as Monday and so on.
Note : the report in the query runs on T+1 basis means if asof date is 21st Apr 2022 means it's actual start time would be 22 Apr 2022, so when it will run on 25th Apr (Monday) the asof date would be 22nd Apr
select* from snap_states
where asof = trunc(sysdate)-1
and upper(system) like ('LOANSL%')
order by start_time;***
If you're skipping weekends, then you could
SQL> with datum (sys_date) as
2 (select date '2022-04-23' from dual union all -- Saturday
3 select date '2022-04-24' from dual union all -- Sunday
4 select date '2022-04-25' from dual union all -- Monday
5 select date '2022-04-26' from dual -- Tuesday
6 )
7 select to_char(sys_date, 'dd.mm.yyyy, Dy') sys_date,
8 trunc(sys_date - case to_char(sys_date, 'Dy', 'nls_date_language = english')
9 when 'Sun' then 2
10 when 'Mon' then 3
11 else 1
12 end) as prev_work_day
13 from datum;
SYS_DATE PREV_WORK_
------------------------ ----------
23.04.2022, Sat 22.04.2022
24.04.2022, Sun 22.04.2022
25.04.2022, Mon 22.04.2022
26.04.2022, Tue 25.04.2022
SQL>
Applied to your query:
select *
from snap_states
where asof = trunc(sysdate - case to_char(sysdate, 'Dy', 'nls_date_language = english')
when 'Sun' then 2
when 'Mon' then 3
else 1
end)
and upper(system) like 'LOANSL%'
order by start_time;

Selecting Dates from Monday to Saturday and Hours Between Morning 8:00:00 AM To Next Day Morning 7:00:00 AM

I am trying to work on a query where there is date selection in the where clause i.e. if sysdate is Monday I have to get the dates from Monday to Saturday and Hours Between Morning 08:00:00 AM to Next Day Morning 07:00:00 AM. I am hardcoding the dates and Hours in the where clause, When I run the query data does not show.
Query:
SELECT TO_CHAR(sysdate, 'HH24:MI:SS'), REPLACE(TO_CHAR(sysdate, 'DAY'), ' ')
FROM dual
WHERE TO_CHAR(sysdate, 'HH24:MI:SS') BETWEEN '08:01:00' AND '08:00:00'
AND TO_CHAR(sysdate, 'DAY') >= 'MONDAY'
AND TO_CHAR(sysdate, 'DAY') <= 'SATURDAY';
You need to filter the wider range first (from 8 AM at Monday till the end of a Saturday, if I understood correctly) and then exclude time from 7 AM till 8 AM.
In the below code iw format element stands for ISO week, that starts on the Monday.
with a as (
select
date '2022-04-17'
+ interval '01:30:00' hour to second
+ interval '2' hour * level
as dt
from dual
connect by level < 90
)
select
to_char(dt, 'yyyymmdd') as day_
, listagg(to_char(dt, 'hh24:mi'), ',')
within group (order by dt asc) as hours
from a
where 1 = 1
/*From Mon 08 AM*/
and dt > trunc(dt, 'iw') +
interval '8' hour
/*Till Sat end of the day*/
and dt < trunc(dt, 'iw') + 6
/*and except minutes between 7 and 8 AM*/
and not (
to_char(dt, 'hh24mi') < '0800'
and to_char(dt, 'hh24mi') > '0700'
)
group by to_char(dt, 'yyyymmdd')
DAY_ | HOURS
:------- | :----------------------------------------------------------------
20220418 | 09:30,11:30,13:30,15:30,17:30,19:30,21:30,23:30
20220419 | 01:30,03:30,05:30,09:30,11:30,13:30,15:30,17:30,19:30,21:30,23:30
20220420 | 01:30,03:30,05:30,09:30,11:30,13:30,15:30,17:30,19:30,21:30,23:30
20220421 | 01:30,03:30,05:30,09:30,11:30,13:30,15:30,17:30,19:30,21:30,23:30
20220422 | 01:30,03:30,05:30,09:30,11:30,13:30,15:30,17:30,19:30,21:30,23:30
20220423 | 01:30,03:30,05:30,09:30,11:30,13:30,15:30,17:30,19:30,21:30,23:30
db<>fiddle here
(And what if sysdate isn't Monday?)
Therefore, could you explain a little bit better what is the input (dates? One date? SYSDATE?) and what is desired output (related to that input).
Basically, I don't understand what you want. Meanwhile, errors you made (if it'll help).
Format model is wrong; this is what you did:
SQL> select to_char(sysdate, 'DAY') day, length(to_char(sysdate, 'DAY')) len from dual;
DAY LEN
--------- ----------
FRIDAY 9
"FRIDAY" doesn't have 9 characters; it has 6 of them --> use the fm format modifier (it'll truncate trailing spaces):
SQL> select to_char(sysdate, 'fmDAY') day, length(to_char(sysdate, 'fmDAY')) len from dual;
DAY LEN
--------- ----------
FRIDAY 6
SQL>
Today (22.04.2022) is Friday. Your query searches for data whose day is between "MONDAY" and "SATURDAY". As you're comparing strings and alphabet goes as [A, B, ..., F, G, ..., M, N, ..., S, T], "F(riday)" is NEVER between M(onday) and S(aturday) so there's zero chance that it'll work.
As of hours: which time exactly is between 08:01 and 08:00? Time doesn't go backwards (unless you meant "08:01 today and 08:00 tomorrow").
if sysdate is Monday I have to get the dates from Monday to Saturday and Hours Between Morning 08:00:00 AM to Next Day Morning 07:00:00 AM.
You can find whether SYSDATE is Monday by comparing the day to the start of the ISO week (which will always be midnight on Monday):
SELECT *
FROM DUAL
WHERE SYSDATE - TRUNC(SYSDATE, 'IW') < 1
You can find out whether the hours are between 08:00 and 07:00 the next day by subtracting 8 hours and finding out whether the time is between 00:00 and 23:00:
SELECT *
FROM DUAL
WHERE (SYSDATE - INTERVAL '8' HOUR) - TRUNC(SYSDATE - INTERVAL '8' HOUR)) DAY TO SECOND
<= INTERVAL '23' HOUR;
You can combine the two to find out if the day is between Monday and Saturday and the time is between 08:00 and 07:00 on the next day (so for Saturday, it would include 7 hours of Sunday) using:
SELECT *
FROM DUAL
WHERE (SYSDATE - INTERVAL '8' HOUR) - TRUNC(SYSDATE - INTERVAL '8' HOUR), 'IW') < 6
AND (SYSDATE - INTERVAL '8' HOUR) - TRUNC(SYSDATE - INTERVAL '8' HOUR)) DAY TO SECOND
<= INTERVAL '23' HOUR;
Note: This does not use TO_CHAR so it is unaffected by any changes to the NLS_TERRITORY or NLS_DATE_LANGUAGE session parameters so it will always give the same answer (independent of the settings of the user who runs the query).
You can use such a combination
SELECT TO_CHAR(dt,'HH24:MI:SS','NLS_DATE_LANGUAGE=English') AS Hour,
TO_CHAR(dt,'Day','NLS_DATE_LANGUAGE=English') AS day
FROM t
WHERE TO_CHAR(dt,'Dy','NLS_DATE_LANGUAGE=English') IN ('Tue','Wed','Thu','Fri')
AND TO_CHAR(dt, 'HH24:MI:SS') NOT BETWEEN '07:00:01' AND '08:00:00'
OR TO_CHAR(dt,'Dy','NLS_DATE_LANGUAGE=English') = 'Mon'
AND TO_CHAR(dt, 'HH24:MI:SS')>= '07:00:00'
OR TO_CHAR(dt,'Dy','NLS_DATE_LANGUAGE=English') = 'Sat'
AND TO_CHAR(dt, 'HH24:MI:SS')<= '08:00:00'
where needs to consider restricting the periods for the bound dates individually
Demo

Get particular date from dual - oracle [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
i want create oracle query using dual to pickup the correct business day
condition:
if 1st day of the month falls on sunday or monday then it need to choose tuesday date
So, if 1st day of month is Sunday or Monday, you want to return the next Tuesday. For all other days, you want to return that day.
Just to set the environment (you don't have to do that):
SQL> alter session set nls_date_language = 'english';
Session altered.
SQL> alter session set nls_date_format = 'dd.mm.yyyy, dy';
Session altered.
Asterisk marks months which reflect the requirement.
SQL> with
2 temp (sys_date) as
3 -- it simulates 15th day of each month in 2021; something what SYSDATE would return
4 (select add_months(date '2021-01-15', level - 1)
5 from dual
6 connect by level <= 12),
7 day (sys_date, day) as
8 -- day name
9 (select sys_date,
10 to_char(trunc(sys_date, 'mm'), 'dy')
11 from temp
12 )
13 select trunc(t.sys_date, 'mm') first_day_of_month,
14 --
15 case when d.day in ('sun', 'mon') then next_day(trunc(t.sys_date, 'mm'), 'tue')
16 else trunc(t.sys_date, 'mm')
17 end result
18 from day d join temp t on trunc(d.sys_date, 'mm') = trunc(t.sys_date, 'mm');
FIRST_DAY_OF_MO RESULT
--------------- --------------------
01.01.2021, fri 01.01.2021, fri
01.02.2021, mon 02.02.2021, tue *
01.03.2021, mon 02.03.2021, tue *
01.04.2021, thu 01.04.2021, thu
01.05.2021, sat 01.05.2021, sat
01.06.2021, tue 01.06.2021, tue
01.07.2021, thu 01.07.2021, thu
01.08.2021, sun 03.08.2021, tue *
01.09.2021, wed 01.09.2021, wed
01.10.2021, fri 01.10.2021, fri
01.11.2021, mon 02.11.2021, tue *
01.12.2021, wed 01.12.2021, wed
12 rows selected.
SQL>
In reality, you'd need a simpler version:
SQL> select case when to_char(trunc(sysdate, 'mm'), 'dy') in ('sun', 'mon') then next_day(trunc(sysdate, 'mm'), 'tue')
2 else trunc(sysdate, 'mm')
3 end result
4 from dual;
RESULT
--------------------
01.10.2021, fri
SQL>
if 1st day of the month falls on sunday or monday then it need to choose tuesday date
As I understand it, if the first day of the current month is either Sunday or Monday then you want to return Tuesday of the current week; otherwise return the current day:
SELECT CASE
WHEN TRUNC(SYSDATE, 'MM') - TRUNC(TRUNC(SYSDATE, 'MM'), 'IW')
IN (0, 6)
THEN TRUNC(SYSDATE) - (TRUNC(SYSDATE) - TRUNC(SYSDATE, 'IW')) + 1
ELSE TRUNC(SYSDATE)
END
FROM DUAL;
Note: this will work in any territory or language.

Oracle SQL get First Business Day date of given date

How can i get the date of the first business day of a given date .
For example:
01-AUG-21 is Sunday, so the first business day is 02-AUG-21 .
You can use a simple case statement in SQL or PL/SQL. In the example below, just replace SYSDATE + LEVEL with the date you would like to use.
SELECT SYSDATE + LEVEL AS some_date,
TO_CHAR (SYSDATE + LEVEL, 'DY') AS some_day_of_week,
SYSDATE
+ LEVEL
+ CASE TO_CHAR (SYSDATE + LEVEL, 'DY') WHEN 'SAT' THEN 2 WHEN 'SUN' THEN 1 ELSE 0 END AS business_day
FROM DUAL
CONNECT BY LEVEL <= 14;
SOME_DATE SOME_DAY_OF_WEEK BUSINESS_DAY
____________ ___________________ ________________
02-JUL-21 FRI 02-JUL-21
03-JUL-21 SAT 05-JUL-21
04-JUL-21 SUN 05-JUL-21
05-JUL-21 MON 05-JUL-21
06-JUL-21 TUE 06-JUL-21
07-JUL-21 WED 07-JUL-21
08-JUL-21 THU 08-JUL-21
09-JUL-21 FRI 09-JUL-21
10-JUL-21 SAT 12-JUL-21
11-JUL-21 SUN 12-JUL-21
12-JUL-21 MON 12-JUL-21
13-JUL-21 TUE 13-JUL-21
14-JUL-21 WED 14-JUL-21
15-JUL-21 THU 15-JUL-21
Here is a PL/SQL example of the same logic
DECLARE
FUNCTION get_business_day (p_date DATE)
RETURN DATE
IS
BEGIN
RETURN TRUNC (
p_date
+ CASE TO_CHAR (p_date, 'DY')
WHEN 'SAT' THEN 2
WHEN 'SUN' THEN 1
ELSE 0
END);
END;
BEGIN
DBMS_OUTPUT.put_line (get_business_day (p_date => SYSDATE));
END;
/
Here's one option: as it is only about 1 week, create a little one-week-calendar and fetch date which is larger than the one entered as a parameter, and which isn't a weekend.
with little_calendar as
(select to_date(:par_datum, 'dd.mm.yyyy') + level - 1 datum
from dual
connect by level <= 7
)
select min(datum)
from little_calendar
where datum > to_date(:par_datum, 'dd.mm.yyyy')
and to_char(datum, 'dy', 'nls_date_language = english')
not in ('sat', 'sun');
A few examples in SQL*Plus:
SQL> with little_calendar as
2 (select to_date('&&par_datum', 'dd.mm.yyyy') + level - 1 datum
3 from dual
4 connect by level <= 7
5 )
6 select min(datum)
7 from little_calendar
8 where datum > to_date('&&par_datum', 'dd.mm.yyyy')
9 and to_char(datum, 'dy', 'nls_date_language = english')
10 not in ('sat', 'sun');
Enter value for par_datum: 01.07.2021 --> the 1st working day after 01.07.2021 (Thursday) ...
MIN(DATUM)
---------------
02.07.2021, fri --> ... is 02.07.2021 (Friday)
SQL> undefine par_datum
SQL> /
Enter value for par_datum: 02.07.2021 --> working day that follows 02.07.2021 (Friday) ...
MIN(DATUM)
---------------
05.07.2021, mon --> ... is 05.07.2021 (Monday)
SQL>

Oracle SQL - Data from previous week (Sunday to Saturday) regardless of when it's run

I'm looking to return data in Oracle for the last full week starting Sunday and finishing Saturday. This needs to be able to run any day of the week.
So I know that this is possible in SQL Server as I have reports that do the exact same thing:-
SET #startdate = DATEADD(wk, -1, DATEADD(wk, DATEDIFF(wk, 0,getdate()), -1))
SET #enddate = DATEADD(wk, DATEDIFF(wk, 0, getdate()), -1)
Today being Friday 17th March the above would return data between Sunday 5th March and Saturday 11th March.
I want to do the same thing in Oracle. Everywhere I've looked so far comes back with results like this:-
SELECT TRUNC (SYSDATE) - (SELECT TO_CHAR (SYSDATE, 'D') FROM DUAL),
TRUNC (SYSDATE) - (SELECT TO_CHAR (SYSDATE, 'D') + 1 FROM DUAL)
FROM DUAL
Or
SELECT SYSDATE AS TODAYS_DATE,
NEXT_DAY (SYSDATE - 7, 'SAT') AS PREV_SATURDAY,
NEXT_DAY (SYSDATE - 7, 'SUN') AS PREV_SUNDAY
FROM DUAL
I'm trying to avoid any 'sysdate-7' type code since that's pretty unwieldy in this situation - can anyone help at all?
Thanks
If, at any given point in time, "previous week" means the seven-day period that ENDS on the most recent midnight at the beginning of a Sunday, then something like this should work:
with inputs (dt) as (
select sysdate from dual union all
select sysdate + 1 from dual union all
select sysdate + 2 from dual union all
select sysdate + 3 from dual
)
-- end of test data; SQL solution begins below this line
select to_char(dt, 'Dy dd-Mon-yyyy hh:mi AM') as dt,
trunc(dt + 1, 'iw') - 8 as prev_wk_start,
trunc(dt + 1, 'iw') - 1 as prev_wk_end
from inputs;
DT PREV_WK_START PREV_WK_END
------------------------ ------------------- -------------------
Fri 17-Mar-2017 10:58 AM 03/05/2017 00:00:00 03/12/2017 00:00:00
Sat 18-Mar-2017 10:58 AM 03/05/2017 00:00:00 03/12/2017 00:00:00
Sun 19-Mar-2017 10:58 AM 03/12/2017 00:00:00 03/19/2017 00:00:00
Mon 20-Mar-2017 10:58 AM 03/12/2017 00:00:00 03/19/2017 00:00:00
Note: Whenever we work with time intervals, we must decide if the endpoints are included. In most cases, the best (and most used) convention is that the start date/time is included, while the end date/time is NOT included. The query above is consistent with this interpretation. If the query is run for an input like date '2017-03-19', which is midnight at the beginning of a Sunday, the query will return the week that ENDS exactly at that date and time. All of this "previous week" strictly precedes the input date/time, because the end point of the week is NOT included in the "one-week interval."
Use TRUNC( date_value, 'IW' ) to do it independent of the NLS_TERRITORY or NLS_DATE_LANGUAGE session parameters:
SELECT SYSDATE AS TODAYS_DATE,
TRUNC( SYSDATE, 'IW' ) AS MONDAY_OF_THIS_ISO_WEEK,
TRUNC( SYSDATE, 'IW' ) - INTERVAL '2' DAY AS PREV_SATURDAY,
TRUNC( SYSDATE, 'IW' ) - INTERVAL '8' DAY AS PREV_SUNDAY
FROM DUAL;
Output:
TODAYS_DATE MONDAY_OF_THIS_ISO_ PREV_SATURDAY PREV_SUNDAY
------------------- ------------------- ------------------- -------------------
2017-03-17 15:45:25 2017-03-13 00:00:00 2017-03-11 00:00:00 2017-03-05 00:00:00