Storing a date as a variable and using it in code (Oracle) - oracle

Oracle Version: 19.2.1.247
Good morning,
I am trying to get the most recent Friday and then store that date to use as a variable. I need to only bring forward those people who have a start date of 28 days or earlier from "my_date". Anyone over 28 days should be excluded.
here is what I have so far:
variable my_date varchar2(30)
exec :my_date := sysdate;
SELECT Distinct * from tbl1.people
where startdt < to_date(:my_date,(select next_day(trunc(sysdate-1), 'FRIDAY'-7 from dual))-35;
I am getting the following error message and I am not sure how to handle it:
ORA-00932: inconsistent datatypes: expected date got date
Here is an example of what I am trying to do:
Last Friday (Oct 23, 2020) is "my_date", so I need to go backwards for 28 days to Sept 25, 2020 and use that as the cutoff date. I do not want to see anyone with Sept 24,2020 or earlier.
The result set should be from Sept 25, 2020 to Oct 23, 2020 only.
Thank you for any help you can give!

To me, it looks as simple as
select *
from your_table
where startdt < next_day(sysdate - 7, 'FRIDAY') - 28;
because
SQL> select next_day(sysdate - 7, 'FRIDAY') - 28 from dual;
NEXT_DAY(S
----------
25.09.2020
SQL>

Related

Creating a loop to compare time between dates (oracle SQL)

Here is an example of data (I'm just sharing an example of one person to hep illustrate my question):
ID
DATE
1
January 1, 2021
1
February 1, 2021
1
March 1, 2021
1
April 1, 2021
1
May 1, 2021
1
June 1, 2021
ID = a unique identifier for a person
DATE = when that person enrolled in a program
A person can enroll in a program for up to 40 days. After that period is up, they become eligible for enrollment again. However, sometimes (for various administrative purposes) someone may enter an enrollment for someone before they are eligible. I am trying to figure out the best way to flag which enrollments were actually eligible.
So for example, the January enrollment is fine because that was the member's first enrollment (so it should be flagged). However, their February enrollment did not occur after 40 days of the prior eligible enrollment (from January), and it should not be flagged. The March enrollment did occur more than 40 days after the eligible January enrollment, so it should be flagged. The April enrollment did not occur after 40 days of the prior eligible enrollment (March) and so it should not be flagged......
I suspect I need to create some sort of loop (I would know how to do this in other programming languages but not SQL. My thinking is along the lines of:
January is the first enrollment so it should be flagged and move to the next row (January stored as the date reference).
February did not occur more than 40 days after the January reference so it should not be flagged, move to the next row (January still the reference).
March did occur more than 40 days after the January reference so it should be flagged, move to the next row (March is now the reference)
.....
Can anyone help me figure out how I could accomplish this?
Here is a brief demo using match_recognize (which has been available since Oracle 12.1). It takes some time to learn it if you don't know it already; if you are familiar with analytic functions and with regular expressions (for strings), the learning will be easier.
First some test data (creating a small table):
create table enrolments (id, enrolment_date) as
select 1, date '2021-01-01' from dual union all
select 1, date '2021-02-01' from dual union all
select 1, date '2021-03-01' from dual union all
select 1, date '2021-04-01' from dual union all
select 1, date '2021-05-01' from dual union all
select 1, date '2021-06-01' from dual union all
select 8, date '2021-01-05' from dual union all
select 8, date '2021-03-10' from dual union all
select 8, date '2021-03-15' from dual union all
select 8, date '2021-04-10' from dual
;
A few notes here - DATE is a reserved keyword, so it can't be a column name (I hope it isn't in your real-life data); in my example the date column is date data type (and I hope it is in your real-life data too, rather than being string data type); and I used the date literal syntax to insert dates.
Then the query and output (and a command to change date formatting in the output to match yours):
alter session set nls_date_format = 'fmMonth dd, yyyy';
select id, enrolment_date, flag
from enrolments
match_recognize(
partition by id
order by enrolment_date
measures nullif(classifier(), 'NOT_ELIGIBLE') as flag
all rows per match
pattern ( ELIGIBLE NOT_ELIGIBLE* )
define NOT_ELIGIBLE as enrolment_date < ELIGIBLE.enrolment_date + 40
);
ID ENROLMENT_DATE FLAG
-- ----------------- -----------
1 January 1, 2021 ELIGIBLE
1 February 1, 2021
1 March 1, 2021 ELIGIBLE
1 April 1, 2021
1 May 1, 2021 ELIGIBLE
1 June 1, 2021
8 January 5, 2021 ELIGIBLE
8 March 10, 2021 ELIGIBLE
8 March 15, 2021
8 April 10, 2021

How to get first line of a string in oracle query?

Expiry Date:
15/05/12-15/07/12
minimum 15 May 2012 maximum 15 July 2012, the exact period in Charterers option
above is the cell value in a column and I need to show the only first line of the value i.e. 15/05/12-15/07/12
To_Char(b.Charter_End_Date, 'DD/MM/RR') || Period_Notice "Expiry Date",
expected result:
15/05/12-15/07/12
actual result:
15/05/12-15/07/12
minimum 15 May 2012 maximum 15 July 2012, the exact period in Charterers option
Substring, up to CHR(10), might be what you're looking for:
SQL> select * From test;
COL
--------------------------------------------------------------------------------
15/05/12-15/07/12
minimum 15 May 2012 maximum 15 July 2012, the exact period in Charterers option
SQL> select substr(col, 1, instr(col, chr(10))) result
2 from test;
RESULT
--------------------------------------------------------------------------------
15/05/12-15/07/12
SQL>

MONTHS_BETWEEN Function

Can someone help me understand the working of Oracle Months_Between Function?
If I query select MONTHS_BETWEEN('02-28-2015', '01-28-2015')
I get an integer value of 1 but if I query
select MONTHS_BETWEEN('02-28-2015', '01-29-2015') I get 0.96.
Refer to the documentation. https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions089.htm
Note - the "31 day month" convention may cause weird results around month-ends. Consider:
select months_between(date '2016-07-02', date '2016-07-01') as one_day,
months_between(date '2016-07-01', date '2016-06-30') as another_day
from dual;
ONE_DAY ANOTHER_DAY
---------- -----------
.032258065 .064516129
1 row selected.
As if June had 31 days. It doesn't, but months_between treats it as though it did.
If you're working with just trying to determine the number of months in a set of months and don't care about the days. I find myself in this situation often... You can do a bit of date manipulation which is rather reliable for determining the number of months in a set of months. Say for instance Jul - Sep while starting with dates.
Thusly:
WITH MONTHS AS (
SELECT
SYSDATE DATE_ONE
, SYSDATE+57 DATE_TWO
FROM DUAL
)
SELECT
m.*
,TO_CHAR(m.DATE_ONE,'MON') START_MONTH
,TO_CHAR(m.DATE_TWO,'MON') END_MONTH
,MONTHS_BETWEEN(m.DATE_TWO,m.DATE_ONE) UNEXPECTED_RESULT
,MONTHS_BETWEEN(LAST_DAY(m.DATE_TWO),LAST_DAY(ADD_MONTHS(m.DATE_ONE,-1))) EXPECTED_RESULT
FROM MONTHS m
;

Change the year of a date to the current year in PL/SQL

I'm currently trying to do a comparison in my select. If the current date is before August 1st of the current year then display august 1st of the last year, otherwise display august 1st of this year. Essentially I'm trying to do:
CASE
WHEN (SYSDATE < 08/01/2015) THEN
08/01/2014
ELSE
08/01/2015
But I am at a loss as to how to get august for the month. So far I have:
TRUNC(SYSDATE, 'MON')
To get /01/ but how would I get it to constantly return august as the month? Would it be better to hardcode in the date and month and dynamically get the year instead? like 01/08/
Try something like this:
1 select sysdate,
2 trunc(sysdate,'YEAR'),
3 add_months(trunc(sysdate,'YEAR'),7),
4 add_months(trunc(sysdate,'YEAR'),7-12)
5* from dual
SQL> /
SYSDATE TRUNC(SYSDA ADD_MONTHS( ADD_MONTHS(
----------- ----------- ----------- -----------
31-jul-2015 01-jan-2015 01-aug-2015 01-aug-2014
SQL>
the columns are:
1) pulling the current sysdate.
2) converting to the first day of the year.
3) adding 7 months to get Aug 1 of current year.
4) -12 months to get Aug 1 of last year.
(that shows you the usage, you can figure out how to plug those suckers into your CASE statement ;) )

Oracle - Date substraction in where clause

I'm trying to figure out how to compare the result of a date substraction in a where clause.
Clients subscribed to a service and therefore are linked to a subscription that has an end date. I want to display the list of subscriptions that will come to an end within the next 2 weeks.
I did not designed the databse but noticed that the End_Date column type is a varchar and not a date.. I can't change that.
My problem is the following:
If I try to select the result of the substraction for example with this request:
SELECT(TO_DATE(s.end_date,'YYYY-MM-DD') - TRUNC(SYSDATE)) , s.name
from SUBSCRIPTION s WHERE s.id_acces = 15
This will work and give me the number of days between the end of the subscription and the current date.
BUT now, if I try to include the exact same request in a clause where for comparison:
SELECT s.name
from SUBSCRIPTION S
WHERE (TO_DATE(s.end_date,'YYYY-MM-DD') - TRUNC(SYSDATE)) between 0 and 16
I will get an error: "ORA-01839 : date not valid for month specified".
Any help would be appreciated..
Somewhere in the table you have your date formatted in a different way from YYYY-MM-DD. In your first query you check a certain row (or a set of rows, s.id_acces = 15), which is probably ok, but in the second you scan through all the table.
Try finding these rows with something like,
select end_date from subscription
where not regexp_like(end_date, '[0-9]{4}-[0-9]{2}-[0-9]{2}')
Check your DD value (ie: day of the month). This value must be between 1 and the number of days in the month.
January - 1 to 31
February - 1 to 28 (1 to 29, if a leap year)
March - 1 to 31
April - 1 to 30
May - 1 to 31
June - 1 to 30
July - 1 to 31
August - 1 to 31
September - 1 to 30
October - 1 to 31
November - 1 to 30
December - 1 to 31
" the End_Date column type is a varchar and not a date.. I can't
change that."
If you can't change the date you'll have to chang3 the data. You can identify the rogue values with this function:
create or replace check_date_format (p_str in varchar2) return varchar2
is
d date;
begin
d := to_date(p_str,'YYYY-MM-DD');
return 'VALID';
exception
when others then
return 'INVALID';
end;
You can use this function in a query:
select sid, end_date
from SUBSCRIPTION
where check_date_format(end_date) != 'VALID';
Your choices are:
fix the data so all the dates are in the same format
fix the data and apply a check constraint to enforce future validity
write a bespoke MY_TO_DATE() function which takes a string and applies lots of different date format masks to it in the hope of getting a successful conversion.

Resources