Compare date in Oracle when some value is null? - oracle

I want to get records that have an expired_date less than "31/12/2015", but some value of expired_date field is null in the database. This is my SQL:
"AND TO_DATE(EXPIRED_DATE,'DD/MM/YYYY') < TO_DATE('31/12/2015','DD/MM/YYYY')"
so I have got an error
"day of the month must between 1 and the last day of the month"
How can I get just records that have an expired_date < '31/12/2015'?
EDIT: The problem is not the null value, but my format in my DB. I store "22-APR-15" and mistake is trying to assign it to 'DD/MM/YYYY' by to_date function

If your column EXPIRED_DATE is of date type then, you don't need to convert it to date again using TO_DATE.
I think you need the following:
AND EXPIRED_DATE < TO_DATE('31/12/2015','DD/MM/YYYY')
This will return false for any EXPIRED_DATE which is null and that record will not be included in the result.
If you want Null EXPIRED_DATE to be included in the result then you can use OR as following:
AND (EXPIRED_DATE IS NULL OR EXPIRED_DATE < TO_DATE('31/12/2015','DD/MM/YYYY'))

Your code ...E(EXPIRED_DATE,'DD/MM/YYY')... has 3 YYY for date instead of 4 YYYY. Tried below and it works
with da(date_a) as (
select '03/04/2015' from dual
union all select '03/04/2015' from dual
union all select '03/04/2017' from dual
union all select '03/04/2015' from dual
union all select '03/04/2016' from dual
union all select NULL from dual
)
SELECT * FROM da WHERE TO_DATE(date_a,'DD/MM/YYYY') < TO_DATE('31/12/2015', 'DD/MM/YYYY');
Even when your date column is in date format it will still work
with da(date_a) as (
select '03/04/2015' from dual
union all select '03/04/2015' from dual
union all select '03/04/2017' from dual
union all select '03/04/2015' from dual
union all select '03/04/2016' from dual
union all select NULL from dual
)
SELECT to_date(date_a, 'DD/MM/YYYY') date_a FROM da WHERE TO_DATE(date_a,'DD/MM/YYYY') < TO_DATE('31/12/2015', 'DD/MM/YYYY');
db<>fiddle

Related

Fix time issue in oracle

Table looks like below:
Expected answer below:
Write an oracle query to fix space and return hh:mm format. Consider same time in a same group and return total count
The WITH clause in the query below is just for testing; remove it, and use the actual table and column names in the main query. Note: count is a reserved keyword, so it can't be a column name. I changed it to count_ (with an underscore).
with
test_data (srt_tm, count_) as (
select '1:00' , 125 from dual union all
select '01:00' , 19000 from dual union all
select ' 01:00', 27 from dual union all
select '4:00' , 22000 from dual union all
select '04:00' , 1800 from dual union all
select ' 04:00', 15000 from dual
)
-- END OF TEST DATA; ACTUAL QUERY BEGINS **BELOW THIS LINE**
select lpad(trim(srt_tm), 5, '0') as srt_tm, sum(count_) as count_
from test_data
group by lpad(trim(srt_tm), 5, '0')
order by srt_tm
;
SRT_TM COUNT_
------ --------
01:00 19152
04:00 38800

REGEXP_SUBSTR not able to process only current row

(SELECT LISTAGG(EVENT_DESC, ',') WITHIN GROUP (ORDER BY EVENT_DESC) FROM EVENT_REF WHERE EVENT_ID IN
( SELECT REGEXP_SUBSTR(AFTER_VALUE,'[^,]+', 1, level) FROM DUAL
CONNECT BY REGEXP_SUBSTR(AFTER_VALUE, '[^,]+', 1, level) IS NOT NULL
)
)
A table from which I am fetching AFTER_VALUE has values of integer which is comma seperated like
AFTER_VALUE data
Expected output
1
Event1
1,2
Event1,Event2
1,12,2,5
Event1,Event12,Event2,Event5
15,13
Event15,Event13
these are Ids in EVENT_REF table which have some description. I am trying to basically present
ex. 1,2 as Event1, Event2 and send back from query. There are multiple events so using REPLACE would be very tedious.
When using above query I'm getting error as “ORA-01722: invalid number” whenever there is more than one value in AFTER_VALUE column Ex. if there exists only one id , then the query works but for values like 1,2 or 1,13 etc it throws invalid number error.
PS: The event names are not Event1,Event2 etc , I have just put for reference.
You don't even need regular expressions for this assignment. Standard string function replace() can do the same thing, and faster. You only need an extra 'Event' at the beginning of the string, since that one doesn't "replace" anything.
Like this: (note that you don't need the with clause; I included it only for quick testing)
with
event_ref (after_value) as (
select '1' from dual union all
select '1,2' from dual union all
select '1,12,2,5' from dual union all
select '15,13' from dual
)
select after_value,
'Event' || replace(after_value, ',', ',Event') as desired_output
from event_ref
;
AFTER_VALUE DESIRED_OUTPUT
----------- -----------------------------
1 Event1
1,2 Event1,Event2
1,12,2,5 Event1,Event12,Event2,Event5
15,13 Event15,Event13
Ah,ok, looks, like you have other characters in your comma-separated list, so you can use this query:
with EVENT_REF(EVENT_ID,EVENT_DESC) as (
select 1, 'Desc 1' from dual union all
select 2, 'Desc 2' from dual union all
select 3, 'Desc 3' from dual union all
select 4, 'Desc 4' from dual union all
select 5, 'Desc 5' from dual union all
select 12, 'Desc12' from dual union all
select 13, 'Desc13' from dual union all
select 15, 'Desc15' from dual
)
select
(SELECT LISTAGG(EVENT_DESC, ',')
WITHIN GROUP (ORDER BY EVENT_DESC)
FROM EVENT_REF
WHERE EVENT_ID IN
( SELECT to_number(REGEXP_SUBSTR(AFTER_VALUE,'\d+', 1, level))
FROM DUAL
CONNECT BY level<=REGEXP_COUNT(AFTER_VALUE, '\d+')
)
)
from (
select '1' AFTER_VALUE from dual union all
select '1,2' AFTER_VALUE from dual union all
select '1,12,2,5' AFTER_VALUE from dual union all
select '15,13' AFTER_VALUE from dual
);
PS. And do not forget that to_number has 'default on conversion error' now: https://docs.oracle.com/en/database/oracle/oracle-database/12.2/sqlrf/TO_NUMBER.html
There is no need to split and concatenate substrings, just use regexp_replace:
with EVENT_REF (AFTER_VALUE) as (
select '1' from dual union all
select '1,2' from dual union all
select '1,12,2,5' from dual union all
select '15,13' from dual
)
select regexp_replace(AFTER_VALUE,'(\d+)','Event\1') from EVENT_REF;
REGEXP_REPLACE(AFTER_VALUE,'(\D+)','EVENT\1')
-----------------------------------------------
Event1
Event1,Event2
Event1,Event12,Event2,Event5
Event15,Event13

Oracle: Is it possible to filter out duplicates only when they appear in succession?

Thank you in advance for your help.
I have a table that holds itinerary information for drivers. There will be times when the itinerary seems to have the same stop (but is several days apart). I'd like to be able to query the table and filter out any record where the address is the same AND the dates are consecutive.
Is this possible?
Thanks again,
josh
with tst as(
select timestamp '2020-08-01 00:00:00' dt, '123 street' loc from dual
union all
select timestamp '2020-08-01 00:00:00', '89 street' from dual
union all
select timestamp '2020-08-02 00:00:00', '456 airport' from dual
union all
select timestamp '2020-08-04 00:00:00', '456 airport' from dual
union all
select timestamp '2020-08-05 00:00:00', '67 street' from dual
union all
select timestamp '2020-08-06 00:00:00', '89 street' from dual
union all
select timestamp '2020-08-07 00:00:00', '123 street' from dual
)
select dt, loc
from (
select dt, loc, nvl(lag(loc) over(order by dt), 'FIRST_ROW') prev_loc
from tst
) where loc <> prev_loc;
fiddle
Another approach would be to use Tabibitosan method which assign consecutive rows a group number and then count number of rows per group.(found in asktom website).
with test_data as(
select date'2020-08-01' dt, '123 street' loc from dual
union all
select date '2020-08-01', '89 street' from dual
union all
select date '2020-08-02', '456 airport' from dual
union all
select date '2020-08-04', '456 airport' from dual
union all
select date '2020-08-05', '67 street' from dual
union all
select date '2020-08-06', '89 street' from dual
union all
select date '2020-08-07', '123 street' from dual
)
select max(dt),loc
from
(
select t.*
,row_number() over (order by dt) -
row_number() over (partition by loc order by dt) grp
from test_data t
)
group by grp,loc
having count(*) > 1;
Another approach using match_recognize available from 12c onwards.patter used {1,} says repeated one or more times
more to learn match_recognize here
with test_data as(
select date'2020-08-01' dt, '123 street' loc from dual
union all
select date '2020-08-01', '89 street' from dual
union all
select date '2020-08-02', '456 airport' from dual
union all
select date '2020-08-04', '456 airport' from dual
union all
select date '2020-08-05', '67 street' from dual
union all
select date '2020-08-06', '89 street' from dual
union all
select date '2020-08-07', '123 street' from dual
)
select *
from test_data
match_recognize (
order by dt
all rows per match
pattern (equal{1,})
define
equal as loc = prev(loc)
);
Playground: Dbfiddle

Have error ORA-01861 when I want to change it to "date column type"

I have a table which calculates some measure base on the Date column type.
also, that should be store date as Persian calendar but at the end when I using TO_DATE function for changing its type have an ORA-01861 error on 19,20,21 days of the May while there is such date is already exist and the second month of Persian calendar has 31 days.
here is my query:
with a as
(select to_char(to_date('20190518', 'YYYYMMDD'),
'YYYYMMDD',
'NLS_CALENDAR=PERSIAN') PERSIAN_DATE
from DUAL
UNION ALL
select to_char(to_date('20190519', 'YYYYMMDD'),
'YYYYMMDD',
'NLS_CALENDAR=PERSIAN') PERSIAN_DATE
from DUAL
UNION ALL
select to_char(to_date('20190520', 'YYYYMMDD'),
'YYYYMMDD',
'NLS_CALENDAR=PERSIAN') PERSIAN_DATE
from DUAL
UNION ALL
select to_char(to_date('20190521', 'YYYYMMDD'),
'YYYYMMDD',
'NLS_CALENDAR=PERSIAN') PERSIAN_DATE
from DUAL
UNION ALL
select to_char(to_date('20190522', 'YYYYMMDD'),
'YYYYMMDD',
'NLS_CALENDAR=PERSIAN') PERSIAN_DATE
from DUAL)
SELECT TO_DATE(PERSIAN_DATE, 'YYYY/MM/DD') pers_date FROM A
my oracle database version is Oracle 12.2.0.1.0
You convert a sting to a DATE, then to a string and back again to a DATE, that's useless.
Try this one:
WITH a AS
(SELECT TO_DATE('20190518', 'YYYYMMDD') PERSIAN_DATE
FROM DUAL
UNION ALL
SELECT TO_DATE('20190519', 'YYYYMMDD') PERSIAN_DATE
FROM DUAL
UNION ALL
SELECT TO_DATE('20190520', 'YYYYMMDD') PERSIAN_DATE
FROM DUAL
UNION ALL
SELECT TO_DATE('20190521', 'YYYYMMDD') PERSIAN_DATE
FROM DUAL
UNION ALL
SELECT TO_DATE('20190522', 'YYYYMMDD') PERSIAN_DATE
FROM DUAL)
SELECT to_char(PERSIAN_DATE, 'YYYYMMDD', 'NLS_CALENDAR=PERSIAN') pers_date
FROM A
or shorter:
SELECT TO_CHAR(TO_DATE('20190517', 'YYYYMMDD')+LEVEL, 'YYYYMMDD', 'NLS_CALENDAR=PERSIAN') pers_date
FROM dual
CONNECT BY LEVEL <= 5;
Or use DATE literals and NLS session values:
ALTER SESSION SET NLS_CALENDAR = 'PERSIAN';
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYYMMDD';
SELECT DATE '2019-05-17' + LEVEL pers_date
FROM dual
CONNECT BY LEVEL <= 5;
When you execute your select just for the 18th (that you were not having an issue with) and adding the raw value you will see it is returning
with a as
(select to_char(to_date('20190518', 'YYYYMMDD'),
'YYYYMMDD',
'NLS_CALENDAR=PERSIAN') PERSIAN_DATE
from DUAL)
SELECT PERSIAN_DATE, TO_DATE(PERSIAN_DATE, 'YYYY/MM/DD') pers_date FROM A;
Persian Date pers_date
13980228 28/FEB/98 00:00:00
So oracle is trying discarding the first two characters and making the best date it can out of the rest (pretty sure this is not the correct Persian date).
So following the same logic when you run this for 19th (we can only get the raw value as to date will error)
with a as
(select to_char(to_date('20190519', 'YYYYMMDD'),
'YYYYMMDD',
'NLS_CALENDAR=PERSIAN') PERSIAN_DATE
from DUAL)
SELECT PERSIAN_DATE pers_date FROM A;
Persian Date
13980229
Following the same logic as above discard the first two characters and you get 29 Feb 1998... this is not a valid date.
As others have stated in the comments Oracle just stores the date it doesn't care about the calendar, you need to convert to the appropriate calendar for display.
For a good explanation of how oracle stores dates see
How are dates stored in Oracle?

oracle query to retrieve closest and second closest future date based on the current date

I want to retrieve the closest and second closest future date based on the current date.
example :
current-date=28-07-2017
dates to be retrieve
28-07-2017
29-07-2017
or followed top two dates which are closest to current date.
plzz help me out in writing this query in oracle
Try this:
select sysdate,sysdate + level "Dates" From DUAL connect by level <= 1 ;
You can do this by using the dense_rank analytic function, like so:
WITH sample_data AS (SELECT 1 ID, to_date('01/07/2017', 'dd/mm/yyyy') dt FROM dual UNION ALL
SELECT 2 ID, to_date('02/07/2017', 'dd/mm/yyyy') dt FROM dual UNION ALL
SELECT 3 ID, to_date('02/07/2017', 'dd/mm/yyyy') dt FROM dual UNION ALL
SELECT 4 ID, to_date('03/07/2017', 'dd/mm/yyyy') dt FROM dual UNION ALL
SELECT 5 ID, to_date('04/07/2017', 'dd/mm/yyyy') dt FROM dual UNION ALL
SELECT 6 ID, to_date('05/07/2017', 'dd/mm/yyyy') dt FROM dual UNION ALL
SELECT 7 ID, to_date('06/07/2017', 'dd/mm/yyyy') dt FROM dual)
SELECT ID, dt
FROM (SELECT ID,
dt,
dense_rank() OVER (ORDER BY dt) dr
FROM sample_data
WHERE dt >= to_date('01/07/2017', 'dd/mm/yyyy'))
WHERE dr <= 2;
ID DT
---------- -----------
1 01/07/2017
2 02/07/2017
3 02/07/2017

Resources