I need to extract the date (08-01-2021) from the below string that has no whitespace
select 'Date-08-01-2021-Trans-1000008-PH.0000-BA-CR-9999.21' from dual
I tried to apply the REGEXP_SUBSTR function as shown below, but using this query I just removed 'Date-'
with x as
(select 'Date-08-01-2021-Trans-1000008-PH.0000-BA-CR-9999.21' as str
from dual)
SELECT REGEXP_SUBSTR(STR, 'Date-([^ ]+)',1,1,'i',1)
FROM x;
Please advise
You are zero-padding the date values so each term has a fixed length and have a fixed prefix so you do not need to use (slow) regular expressions and can just use simple string functions:
SELECT TO_DATE(SUBSTR(value, 6, 10), 'DD-MM-YYYY')
FROM table_name;
(Note: if you still want it as a string, rather than as a date, then just use SUBSTR without wrapping it in TO_DATE.)
For example:
WITH table_name ( value ) AS (
SELECT 'Date-08-01-2021-Trans-1000008-PH.0000-BA-CR-9999.21' FROM DUAL
)
SELECT TO_DATE(SUBSTR(value, 6, 10), 'DD-MM-YYYY') AS date_value
FROM table_name;
Outputs:
DATE_VALUE
08-JAN-21
db<>fiddle here
If the Date- prefix is not going to always be at the start then use INSTR to find it:
WITH table_name ( value ) AS (
SELECT 'Date-08-01-2021-Trans-1000008-PH.0000-BA-CR-9999.21' FROM DUAL UNION ALL
SELECT 'Trans-1000008-Date-08-02-2021-PH.0000-BA-CR-9999.21' FROM DUAL
)
SELECT TO_DATE(SUBSTR(value, INSTR(value, 'Date-') + 5, 10), 'DD-MM-YYYY') AS date_value
FROM table_name;
Which outputs:
DATE_VALUE
08-JAN-21
08-FEB-21
If you can have multiple Date- substrings and you want to find the one that is either at the start of the string or has a - prefix then you may need regular expressions:
WITH table_name ( value ) AS (
SELECT 'Date-08-01-2021-Trans-1000008-PH.0000-BA-CR-9999.21' FROM DUAL UNION ALL
SELECT 'TransDate-1000008-Date-08-02-2021-PH.0000-BA-CR-9999.21' FROM DUAL
)
SELECT TO_DATE(
REGEXP_SUBSTR(value, '(^|-)Date-(\d\d-\d\d-\d{4})([-.]|$)', 1, 1, 'i', 2),
'DD-MM-YYYY'
) AS date_value
FROM table_name;
db<>fiddle here
Just use a more precise regular expression:
SELECT REGEXP_SUBSTR(STR, 'Date-([0-9]{2}-[0-9]{2}-[0-9]{4})', 1, 1, 'i', 1)
FROM x;
Or for less accuracy but more conciseness:
SELECT REGEXP_SUBSTR(STR, 'Date-([-0-9]{10})', 1, 1, 'i', 1)
Related
I have hit the wall. I need to extract the from: Date and the To: Date from the text below.
It will always start with the date. But the changed from: and changed to: can sometimes be [none] instead of the date pattern.
Column:
"31-Jul-2017 11:29 (GMT+1:00) Date Due was modified by: placeholder, SNO-1234.
**Changed from: 12-Dec-2017**
**Changed to: 01-Jan-2021**
Meaning: Initial Entry
Meaning Comments:"
Wanted output:
Column = From: "12-Dec-2017"
Column = Changed To: "01-Jan-2021"
I have tried
cast(regexp_substr(column:, 'from:\s.*') as varchar(17)) as "From:",
cast(regexp_substr(column:, 'to:\s.*') as varchar(15)) as "Changed To:"
and i end up with..
Column = From: "**From:** 12-Dec-2017"
Column = Changed To: "**To:** 01-Jan-2021"
Anyway to exclude the from: and to: ? so basicly, how can i ONLY get the 11 characters after "from: " and "to: ".
Thanks in advance!
\GloriousWater
You don't need a regular expression, you can use simple string functions:
SELECT CASE
WHEN from_pos > 0
THEN TO_DATE(
SUBSTR( value, from_pos + 14, 11 )
DEFAULT NULL ON CONVERSION ERROR,
'DD-MON-YYYY'
)
END AS from_dt,
CASE
WHEN to_pos > 0
THEN TO_DATE(
SUBSTR( value, to_pos + 12, 11 )
DEFAULT NULL ON CONVERSION ERROR,
'DD-MON-YYYY'
)
END AS to_dt
FROM (
SELECT value,
INSTR( LOWER( value ), 'changed from: ', 1, 1 ) AS from_pos,
INSTR( LOWER( value ), 'changed to: ', 1, 1 ) AS to_pos
FROM table_name
);
However, if you did want to use (slower) regular expressions then you could use:
SELECT TO_DATE(
REGEXP_SUBSTR(
value,
'changed from:\s*(([012]\d|3[01])-(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)-\d{4})',
1,
1,
'i',
1
)
DEFAULT NULL ON CONVERSION ERROR,
'DD-MON-YYYY'
) AS from_dt,
TO_DATE(
REGEXP_SUBSTR(
value,
'changed to:\s*(([012]\d|3[01])-(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)-\d{4})',
1,
1,
'i',
1
)
DEFAULT NULL ON CONVERSION ERROR,
'DD-MON-YYYY'
) AS to_dt
FROM table_name;
Which, for your sample data:
CREATE TABLE table_name ( value ) AS
SELECT '31-Jul-2017 11:29 (GMT+1:00) Date Due was modified by: placeholder, SNO-1234.
Changed from: 12-Dec-2017
Changed to: 01-Jan-2021
Meaning: Initial Entry Meaning Comments:' FROM DUAL
Both output:
FROM_DT | TO_DT
:------------------ | :------------------
2017-12-12T00:00:00 | 2021-01-01T00:00:00
db<>fiddle here
I have a script:
I write the data in test2!
INSERT INTO test2 (val)
SELECT SUBSTR (:P1_FIRST, 1, 1)
|| TO_CHAR (
( TO_NUMBER (REGEXP_SUBSTR (:P1_FIRST, '\d+$'))
+ LEVEL
- 1))
AS val
FROM dual
CONNECT BY LEVEL <=
TO_NUMBER (
REGEXP_SUBSTR (:P1_LAST, '\d+$'))
- TO_NUMBER (
REGEXP_SUBSTR (:P1_FIRST, '\d+$'))
+ 1;
I want to write it in another table myself.I want to write it down all in one request
INSERT INTO test3 (val,data_area,data_add )
SELECT SUBSTR (:P1_FIRST, 1, 1)
|| TO_CHAR (
( TO_NUMBER (REGEXP_SUBSTR (:P1_FIRST, '\d+$'))
+ LEVEL
- 1))
AS val,
(select data_area from SEC_USERS_LIST where login = LOWER(:APP_USER)) as data_area,
(select to_char(sysdate, 'dd.mm.yyyy hh24:mi:ss') "Right Now" from dual ) as data_add
FROM dual
CONNECT BY LEVEL <=
TO_NUMBER (
REGEXP_SUBSTR (:P1_LAST, '\d+$'))
- TO_NUMBER (
REGEXP_SUBSTR (:P1_FIRST, '\d+$'))
+ 1;
You could try and use Oracle INSERT ALL syntax :
In a multitable insert, you insert computed rows derived from the rows returned from the evaluation of a subquery into one or more tables.
ALL into_clause
Specify ALL followed by multiple insert_into_clauses to perform an unconditional multitable insert. Oracle Database executes each insert_into_clause once for each row returned by the subquery.
Consider:
INSERT ALL
INTO test3 (val,data_area,data_add) VALUES (x_val, x_data_area, x_data_add)
INTO test2 (val) VALUES (x_val)
SELECT SUBSTR (:P1_FIRST, 1, 1)
|| TO_CHAR (
( TO_NUMBER (REGEXP_SUBSTR (:P1_FIRST, '\d+$'))
+ LEVEL
- 1))
AS x_val,
(select data_area from SEC_USERS_LIST where login = LOWER(:APP_USER)) as x_data_area,
(select to_char(sysdate, 'dd.mm.yyyy hh24:mi:ss') "Right Now" from dual ) as x_data_add
FROM dual
CONNECT BY LEVEL <=
TO_NUMBER (REGEXP_SUBSTR (:P1_LAST, '\d+$'))
- TO_NUMBER (REGEXP_SUBSTR (:P1_FIRST, '\d+$'))
+ 1
;
NB: as far as concerns, the inline subquery that returns the current date is not needed and could be simplified as just:
to_char(sysdate, 'dd.mm.yyyy hh24:mi:ss') as x_data_add
Instead of:
(select to_char(sysdate, 'dd.mm.yyyy hh24:mi:ss') "Right Now" from dual ) as x_data_add
Even more, as commented by Thomas Tschernich: if x_data_add is of datatype DATE, no casting should be necessary and you could simply use a default on the table column and omit it from the INSERT completely
Have a text string that I need to convert to date, however, it's not the generic yyyymmdd string. e.g. March 2018 is stored as '2018m3' as are all other dates. Is there a way to convert the string to a normal date in Oracle?
This may be helpful :
SQL> SELECT to_date('2018m3', 'YYYY"m"MM') FROM dual
2 /
Output
TO_DATE('2018M3','YYYY"M"MM')
-----------------------------
3/1/2018
If that is the format, you can do:
select add_months(to_date(substr(col, 1, 4) || '0101'),
to_number(replace(substr(col, -2), 'm', '')) - 1
)
An alternative method:
select to_date(substr(col, 1, 4) || regexp_substr(col, '[0-9]+', 1, 2), 'YYYYMM')
I have this string
abcd-0490-abcd
I am getting the number part using substr('abcd-0490-abcd',5,4) but the problem is that I want to increment that number by one so I add the to_number like that
to_number(substr('abcd-0490-abcd', 5,4 ))
which will remove the zero number at the left hand that's coz an error to my script so its not catching correct data i want
anyway to avoid this
create or replace procedure pro
(yy in varchar2 default '[0-9]{2}',mm in varchar2 default '[0-9]{2}') as pattern varchar2(80);
cursor cur (pattern varchar2) is
with t as
(
select
substr(column1, 5,4 ) as seq,
substr(column1, 10, 2) as yy,
substr(column1, 13, 2) as mm,
substr(column1, 16, 2) as dd
from test1
where regexp_like(column1, pattern)
),
r (yy, mm, dd, seq, max_seq) as (
select yy, mm, dd, min(seq), max(seq)
from t
group by yy, mm, dd
union all
select yy, mm, dd, seq + 1, max_seq
from r
where seq + 1 <= max_seq
)
select yy, mm, dd, seq as missing_seq
from r
where not exists (
select 1 from t
where t.yy = r.yy
and t.mm = r.mm
and t.dd = r.dd
and t.seq = r.seq
)
order by yy, mm, dd, seq;
begin
pattern := 'Cabcd[-][0-9]{4}[_][0-9]{2}'|| yy ||'[_][0-9]{2}' || mm || '[_][0-9]{2}[_][0-9]{4}[_][T]["2"]';
for rec in cur(pattern) loop
dbms_output.put_line(rec.missing_seq);
end loop;
dbms_output.put_line('Done');
end pro;
/
Use LPAD function:http://docs.oracle.com/cd/E11882_01/server.112/e26088/functions095.htm#SQLRF00663
select lpad( to_number(substr('abcd-0490-abcd', 6,4 )) + 1 , 4, '0')
from dual;
demo: http://sqlfiddle.com/#!4/d41d8/25730
The default number-to-character conversion does not produce leading zeros.
Do an explicit TO_CHAR to define your format:
SELECT TO_CHAR(490, 'FM0000') FROM dual;
0490
Edit: This is an example with your query.
SELECT TO_CHAR( TO_NUMBER( SUBSTR( 'abcd-0490-abcd', 6, 4 ) ) + 1, 'FM0000' )
FROM dual;
0491
better u try " rownum " for increment
SELECT TO_CHAR( TO_NUMBER( SUBSTR( 'abcd-0490-abcd', 6, 4 ) ) +rownum, 'FM0000' )
FROM dual;
use table dual instead of any other table in ur database.
SELECT TO_CHAR( TO_NUMBER( SUBSTR( 'abcd-0490-abcd', 6, 4 ) ) +rownum, 'FM0000' )
FROM 'your_tablename';
I'd like to format a number as "1st", "2nd", "4th", "9th", etc. Is there an Oracle function that will do this for me?
Assuming the value supplied is numeric, rather than DATE, you can use TO_CHAR but you have to convert the numeric value to a string, then a DATE (Julian) before ultimately formatting it:
SELECT TO_CHAR(TO_DATE('1', 'dd'), 'ddth')
FROM DUAL
Result:
01st
When testing, using 'd' for the format didn't return expected results because the value is interpreted as a Julian date. Either substring the output to remove the leading zero, or provide a full date string (doesn't matter to the TO_CHAR because it's only interested in the day of the month):
SELECT TO_CHAR(TO_DATE('1900-01-01', 'YYYY-MM-dd'), 'dth')
FROM DUAL
Because calendar days end at 31, use the year value instead to handle numbers greater than 31:
SELECT TO_CHAR(TO_DATE('32-01-01', 'YYYY-MM-dd'), 'yyth')
FROM DUAL
Result:
32nd
Maybe I'm oversimplifying, but it seems like the following should work just fine (for integers) and is a lot more readable than converting to a date and back:
select case
when initial_extent is null then null
when substr(initial_extent,-2,1) = '1'
then initial_extent || 'th'
else case substr(initial_extent,-1,1)
when '1' then initial_extent || 'st'
when '2' then initial_extent || 'nd'
when '3' then initial_extent || 'rd'
else initial_extent || 'th'
end
end as formatted_number
from user_tables
select substr( to_char( to_date( abs( decode( mod( l_value, 10 ), 0, 4, mod( l_value , 10 ) ) ), 'YYYY' ), 'YTH' ), 2 ) as value
from dual
Replace l_value with appropriate, hmmm, value. Should cover any numbers.