Regex_subtr in oracle sql - sql

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

Related

How to extract date from text string

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)

Replacing specific date with a empty value in Oracle denodo

I have a view where I am selecting a date column and reading it as a local date in denodo. In my column, I have a date '00-Jan-1900' like this which is giving issue so I wanted to replace this with empty.
When I run this query, I get results as shown in the table2. But what I want is just an empty record in place of this date and looking like TABLE3.
Query:
select
to_localdate('dd-MM-yyyy', substring(replace("DATE", '00-Jan-1900', ''), 0, 10)) AS dtm
from XX
Table1:
DATE
22-Dec-2016
00-Jan-1900
30-Sep-2014
After replacing the date i get
Table2:
DATE
0201-12-22
0201-09-30
Is there a way i get output table like this?
TABLE3:
DATE
22-Dec-2016
30-Sep-2014
In my column, I have a date '00-Jan-1900' like this which is giving issue so I wanted to replace this with empty.
In Oracle, you can use:
UPDATE table_name
SET value = NULL
WHERE EXTRACT( DAY FROM value ) = 0;
If you have the table:
CREATE TABLE table_name ( value DATE );
Then, some extra hoops need to be jumped through to insert an invalid date (namely generating the date from binary data so that the normal validation process for date values can be skipped) by creating this function:
CREATE FUNCTION createDate(
year int,
month int,
day int,
hour int,
minute int,
second int
) RETURN DATE DETERMINISTIC
IS
hex CHAR(14);
d DATE;
BEGIN
hex := TO_CHAR( FLOOR( year / 100 ) + 100, 'fm0X' )
|| TO_CHAR( MOD( year, 100 ) + 100, 'fm0X' )
|| TO_CHAR( month, 'fm0X' )
|| TO_CHAR( day, 'fm0X' )
|| TO_CHAR( hour + 1, 'fm0X' )
|| TO_CHAR( minute + 1, 'fm0X' )
|| TO_CHAR( second + 1, 'fm0X' );
DBMS_OUTPUT.PUT_LINE( hex );
DBMS_STATS.CONVERT_RAW_VALUE( HEXTORAW( hex ), d );
RETURN d;
END;
/
Then, you can have the data:
INSERT INTO table_name ( value )
SELECT DATE '1900-01-01' FROM DUAL UNION ALL
SELECT createDate( 1900, 1, 0, 0, 0, 0 ) FROM DUAL;
and:
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
SELECT value,
TO_CHAR( value, 'YYYY-MM-DD HH24:MI:SS' ) AS string_value
FROM table_name;
Outputs:
VALUE | STRING_VALUE
:------------------ | :------------------
1900-01-01 00:00:00 | 1900-01-01 00:00:00
1900-01-00 00:00:00 | 0000-00-00 00:00:00
To get rid of your invalid value, you can use:
UPDATE table_name
SET value = NULL
WHERE EXTRACT( DAY FROM value ) = 0;
Then:
SELECT value,
TO_CHAR( value, 'YYYY-MM-DD HH24:MI:SS' ) AS string_value
FROM table_name;
Outputs:
VALUE | STRING_VALUE
:------------------ | :------------------
1900-01-01 00:00:00 | 1900-01-01 00:00:00
null | null
db<>fiddle here
Is there a way i get output table like this?
TABLE3:
DATE
22-Dec-2016
30-Sep-2014
You can use:
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-Mon-YYYY';
And then just run your query and it should change the output (but you will need to make sure the format is set in future sessions when you run the query).
Or you can specify the format model using TO_CHAR:
SELECT TO_CHAR( your_column, 'DD-Mon-YYYY' ) AS your_column
FROM your_table

how to sum up minutes and seconds ?in oracle

I have a column called duration_d which is varchar2 and the data in that table looks like below
duration_d
-----------
12:25
01:35
12:10
04:21
12:18
12:24
I tried below query
SELECT SUM( to_date( duration_d, 'mi:ss' ))
FROM table
GROUP BY calling_number;
When I execute it following error is coming
ORA-00933: SQL command not properly ended
00933. 00000 - "SQL command not properly ended"
can any one tell me how to make sum it?
To get the total as fractions of a day you can use:
SELECT SUM( TO_DATE( duration_d, 'MI:SS' ) - TO_DATE( '00:00', 'MI:SS' ) ) AS total
FROM your_table
Which gives the result:
TOTAL
------------------------------------------
0.0383449074074074074074074074074074074074
To convert this to an interval data type you can use NUMTODSINTERVAL:
SELECT NUMTODSINTERVAL(
SUM( TO_DATE( duration_d, 'MI:SS' ) - TO_DATE( '00:00', 'MI:SS' ) ),
'DAY'
) AS total
FROM your_table
Which gives the result:
TOTAL
-------------------
+00 00:55:13.000000
Please try below:
with x as
(select sum((regexp_substr(YOUR_COLUMN, '[0-9]+', 1, 1)*60) +
regexp_substr(id, '[0-9]+', 1, 2)) seconds
from YOUR_TABLE)
SELECT
TO_CHAR(TRUNC(seconds/3600),'FM9900') || ':' ||
TO_CHAR(TRUNC(MOD(seconds,3600)/60),'FM00') || ':' ||
TO_CHAR(MOD(seconds,60),'FM00')
FROM x
Will work only if the duration is always [MI:SS].
Also you can add the group by as per your requirement.
Converting Seconds to the required duration format Reference.
Group By
with x as
(select calling_number,sum((regexp_substr(YOUR_COLUMN, '[0-9]+', 1, 1)*60) +
regexp_substr(id, '[0-9]+', 1, 2)) seconds
from YOUR_TABLE
group by calling_number)
SELECT calling_number,
TO_CHAR(TRUNC(seconds/3600),'FM9900') || ':' ||
TO_CHAR(TRUNC(MOD(seconds,3600)/60),'FM00') || ':' ||
TO_CHAR(MOD(seconds,60),'FM00')
FROM x
Use a combination of SUBSTR, to_char, to_date, NVL, INSTR, reverse and SUM.
SELECT "calling_number",
to_char(to_date(SUM(NVL(SUBSTR("duration_d", 0, INSTR("duration_d", ':')-1), "duration_d"))*60 +
SUM(substr("duration_d", - instr(reverse("duration_d"), ':') + 1)),'sssss'),'hh24:mi:ss') AS SUM_DURATION_D
FROM yourtable
GROUP BY "calling_number"
Output
calling_number SUM_DURATION_D
1 00:26:10
2 00:29:03
SQL Fiddle: http://sqlfiddle.com/#!4/9b0a81/33/0
Correct spelling as below
SELECT SUM( TO_DATE( duration_d, 'mi:ss' ) )
FROM YOURTABLE Group By calling_number

Convert DB2 SQL Decimal to DATE

I need to convert Decimal to date. I have a decimal date field that contains data like this :
1,132,009.00 --1/13/2009
7,152,004.00 --7/15/2004
11,012,005.00 --11/01/2005
etc
I would like it to read as xx/xx/xxxx.
Is there anyway to do this with SQL commands or DB2 logic in a select statement?
SELECT column1 from table1 ;
WITH x(decvalue) AS ( VALUES (DECIMAL(1132009.00)),(DECIMAL(7152004.00)),(DECIMAL(11012005.00)) )
SELECT CAST(
LPAD( RTRIM( CHAR( INTEGER( decvalue/1000000 ))), 2, '0' ) || '/' ||
LPAD( RTRIM( CHAR( MOD( decvalue/10000, 100 ))), 2, '0' ) || '/' ||
MOD( decvalue, 10000 )
AS CHAR(10))
AS chardateresult
FROM x
;
Using the same WITH values as #Fred, I came up with:
WITH x(decvalue) AS ( VALUES (DECIMAL(1132009.00)),(DECIMAL(7152004.00)),(DECIMAL(11012005.00)) )
SELECT TO_DATE(CHAR(CAST(decvalue AS DECIMAL(8,0))), 'MMDDYYYY')
FROM x
This assumes that your input values aren't going to be longer than 8 digits (2 for month, 2 for day, 4 for year), otherwise you'll get an overflow error on the cast. It will also fail if there's not at least some value for each of month, day, and year (00002011 would not work, for example, but 01012011 would).

In Oracle, how do I convert a number such as 1 to a string such as "1st"?

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.