I want a regexp to match partial weekday names. For example, I want to match "Thursday", "thurs", "thur" or "Thu". I tried "thu(rsday)?", but that only matched "thu" and "thursday". The complete regular expression to match abbreviated weekdays would be excessively long. I tried this regex string:
Mon(day)?|Tue(sday)?|Wed(nesday)?|Thu(rsday)?|Fri(day)?|Sat(urday)?|Sun(day)?
The strings I have look like this:
3-Dec Mon 1:00pm Premiere USPHL Sk3-Red
4-Dec Tue 8:10pm U16 USPHL Sk3-Red
6-Dec Thur 1:00pm Premiere USPHL Sk3-Red
In Oracle I'd do this where I want to select where the string matches a weekday. I suspect you can tweak the regular expression to fit your environment:
with tbl(str) as (
select '3-Dec Mon 1:00pm Premiere USPHL Sk3-Red' from dual union all
select '4-Dec Tues 8:10pm U16 USPHL Sk3-Red' from dual union all
select '6-Dec Thursday 1:00pm Premiere USPHL Sk3-Red' from dual
)
select str
from tbl
where regexp_like(str, ' (mon|tue(s)?|wed(nes)?|Thu(r)?(s)?|fri|sat(ur)?|sun)(day)? ', 'i');
OK, thanks for the help. I ended up with this regexp:
(?i)((mon|tue(s)?|wed((((n)?e)?s)?)?|thu(r)?(s)?|fri|sat(ur)?|sun)(day)?)
A little more complicated than I wanted, but it works. I'll need plenty of comments in my code ;-)
Related
I need to convert a string to a date field. The field stores 30 characters. Dates, when present, are formatted as 'yyyymmdd' (20170202). In all cases, dates have 22 spaces after. I need to format this field as a date field like this: dd-mm-yyyy.
I've tried several formulas:
TO_CHAR(PERSACTION.NEW_VALUE_02, 'dd-mm-yyyy') ,TO_CHAR(PERSACTION.NEW_VALUE_02, 'yyyymmdd'), trim(TO_CHAR(PERSACTION.NEW_VALUE_02, 'yyyymmdd')) with error message: invalid number format model. Your expertise is welcome and appreciated.
to_char(to_date( rtrim(new_value_02), 'yyyymmdd'), 'dd-mm-yyyy')
Should do the trick. rtrim removes spaces on right side of string. Then I convert it to date using the date format specified, and then convert it to a string again in the desired format.
Did tried to convert to date format and then to char again?
TO_CHAR(TO_DATE(PERSACTION.NEW_VALUE_02,'yyyymmdd'),'dd-mm-yyyy')
Please, please, please do not store DATEs and CHARACTER datatypes. This will only lead to issues that can be avoided when using the DATE datatype.
If you want to change the string 20170202 to another string and not actually a date (which would have no intrinsic formatted text representation), you could optionally use a regular expression to transform it, instead of converting to a date and back:
select regexp_replace('20170202 ', '^(\d{4})(\d{2})(\d{2}) +$', '\3-\2-\1')
from dual;
REGEXP_REPLACE(
---------------
02-02-2017
Or you could use substr instead of regexp_substr, which may perform better even if you have to call it three times; using a CTE just to avoid repeating the value:
with t(str) as (
select '20170202 ' from dual
)
select substr(str, 7, 2) ||'-'|| substr(str, 5, 2) ||'-'|| substr(str, 1, 4)
from t;
SUBSTR(STR
----------
02-02-2017
If you do convert to a date and back you would uncover any values which cannot be converted, as they will cause an exception to be thrown. That would imply you have bad data; which would have been avoided by using the right data type in the first place, of course. These will convert any old rubbish, with varying results depending on how far the strings stray from the pattern you expect - but including strings like '20170231' which represent an invalid date. And null value or strings of just spaces will be converted to odd things with the substr version, but you could filter those out.
You can see the kind of variation you would get with some sample data that doesn't match your expectations:
with t(str) as (
select '20170202 ' from dual
union all select '20170231 ' from dual
union all select '2017020c ' from dual
union all select '2017020 ' from dual
union all select '201702021 ' from dual
union all select ' ' from dual
union all select null from dual
)
select str,
regexp_replace(str, '^(\d{4})(\d{2})(\d{2}) +$', '\3-\2-\1') as reg,
substr(str, 7, 2) ||'-'|| substr(str, 5, 2) ||'-'|| substr(str, 1, 4) as sub
from t;
STR REG SUB
------------- ------------- -------------
20170202 02-02-2017 02-02-2017
20170231 31-02-2017 31-02-2017
2017020c 2017020c 0c-02-2017
2017020 2017020 0 -02-2017
201702021 201702021 02-02-2017
- -
--
With the anchors and whitespace expectation, the regular expression doesn't modify anything that doesn't consist entirely of 8 numeric characters. But it can still form invalid 'dates'.
SELECT TO_DATE('1st March 2017','DD MON YYYY') from dual
It's ok with SELECT TO_DATE(01 March 2017','DD MON YYYY') from dual , doesn't like the 'st'
I think you need something like regex:
SELECT TO_DATE(
regexp_replace('1st March 2017','^(\d+)\w+','\1')
,'DD MON YYYY') from dual
Alas, you can't do it directly: https://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements004.htm#BABGDDFB
Notes on date format element suffixes:
When you add one of these suffixes to a datetime format element, the return value is always in English.
Datetime suffixes are valid only to format output. You cannot use them to insert a date into the database.
Which actually means they work only with to_char() to transform a date into a string; they don't work with to_date() to convert a string to a date.
So you will have to play dirty tricks - perhaps regexp_replace to get rid of st. Like Michael has shown already.
Hope here is your answer
SELECT to_date('1st march 2017','dd"st" month yyyy') "Date" FROM dual;
Date
01-MAR-17
SELECT TO_CHAR(to_date('1st march 2017','dd"st" month yyyy'),'dd-mon-yyyy') "Date"
FROM dual;
Date
01-mar-2017
I have a column in which a string starts with - 'Chicago, IL, April 20, 2015 — and so on text here'. I want to extract the Date part from this string in Oracle. Any ideas on how to do this. I was able to find something for mm/dd/yyyy like below, but not for long date format.
SELECT REGEXP_SUBSTR(' the meeting will be on 8/8/2008', '[0-9]{1,}/[0-9]{1,}/[0-9]{2,}') FROM dual
You could use:
SELECT TO_DATE(
REGEXP_SUBSTR(
'Chicago, IL, April 20, 2015 — and so on text here',
'(JANUARY|FEBRUARY|MARCH|APRIL|MAY|JUNE|JULY|AUGUST|SEPTEMBER|'
|| 'OCTOBER|NOVEMBER|DECEMBER)'
|| '[[:space:]]+([012]?[0-9]|3[01])'
|| '[[:punct:][:space:]]+\d{4}',
1,
1,
'i'
),
'MONTH DD YYYY'
)
FROM DUAL;
If you want to validate the dates as well (so you don't get an error for February 29, 2001) then you could use a user-defined function:
CREATE FUNCTION parse_Date(
in_string VARCHAR2,
in_format VARCHAR2 DEFAULT 'YYYY-MM-DD',
in_nls_params VARCHAR2 DEFAULT NULL
) RETURN DATE DETERMINISTIC
AS
BEGIN
RETURN TO_DATE( in_string, in_format, in_nls_params );
EXCEPTION
WHEN OTHERS THEN
RETURN NULL;
END;
/
And replace the TO_DATE( ... ) function with PARSE_DATE( ... )
If your columns value is always start with 'Chicago, IL, April 20, 2015 — and so on text here' then you could simly use SUBSTR instead of REGEXP_SUBSTR
SELECT
SUBSTR(column_name
,INSTR(column_name, ',', 1, 2) + 1
,INSTR(column_name, '—') - INSTR(column_name, ',', 1, 2) - 1
)
FROM
dual;
If not then you could use REGEXP_SUBSTR as other answer mention, my original answer is wrong as #MTO comment
Well, you can take a direct approach and use a regular expression like in the example that you've found:
SELECT
REGEXP_SUBSTR('Chicago, IL, April 20, 2015 - etc etc', '(January|February|March|April|May|June|July|August|September|October|November|December) [0-9]{1,2}, [0-9]{4}')
FROM dual;
But this will only work properly if all the dates are in the exact same format. Full month name with first letter uppercased, space, day, comma, space, 4-digit year. If there can be more than one space or no space at all, use \s* instead of spaces in the regular expression. If the month name isn't necessarily initcap, use initcap() on source or case-insensitive flag for regexp_substr function.
Additionally, this will catch bogus dates that fit the format, like "April 99, 1234", you'll have to filter them later.
I change OCI8 version for PHP and since this query dosn't work :
SELECT 'M'||to_char(to_date(OD.DATE2,'DD/MM/YYYY'),'MM') PERIODE, count(*) DATA, OD.DCCPT DCCPT
FROM BDD OD
WHERE BDD = 'phone'
AND OD.SENS = 'Ent'
AND OD.DCCPT IN('PIOLUC')
AND (OD.DATE2 BETWEEN '08/03/2015' AND '08/03/2016')
group by 'M'||to_char(to_date(OD.DATE2,'DD/MM/YYYY'),'MM'), OD.CDCCPT CDCCPT
I got this message :
Message: oci_execute(): ORA-01843: not a valid month
It works with Toad for Oracle 11. Do you have any solution ?
Thank you :)
Looking at the line:
OD.DATE2 BETWEEN '08/03/2015' AND '08/03/2016'
Then '08/03/2015' and '08/03/2016' are string literals and are not dates.
Oracle will attempt an implicit conversion from a string literal to a date using your NLS_DATE_FORMAT session parameter as the format mask and if this does not work it will throw an error.
The simple solution is not to use string literals but use date literals instead:
SELECT 'M'||to_char( DATE2, 'MM' ) PERIODE,
count(*) DATA,
DCCPT
FROM BDD
WHERE BDD = 'phone'
AND SENS = 'Ent'
AND DCCPT IN ( 'PIOLUC' )
AND DATE2 BETWEEN DATE '2015-03-08' AND DATE '2016-03-08'
GROUP BY
'M'||to_char( DATE2, 'MM' ),
DCCPT
But you could also specify the format mask:
OD.DATE2 BETWEEN TO_DATE( '08/03/2015', 'DD/MM/YYYY' ) AND TO_DATE( '08/03/2016', 'DD/MM/YYYY' )
Assuming that OD.DATE2 is of DATE datatype, you need to explicitly convert the dates-as-strings (eg. '08/03/2015') into date format.
Something like:
SELECT 'M'||to_char(OD.DATE2,'MM') PERIODE,
count(*) DATA,
OD.DCCPT DCCPT
FROM BDD OD
WHERE BDD = 'phone'
AND OD.SENS = 'Ent'
AND OD.DCCPT IN ('PIOLUC')
AND OD.DATE2 BETWEEN to_date('08/03/2015', 'dd/mm/yyyy') AND to_date('08/03/2016', 'dd/mm/yyyy')
group by 'M'||to_char(OD.DATE2,'MM'),
OD.DCCPT;
Note how I have removed the to_date from around OD.DATE2 in the select and group by lists, since it is a very bad idea to use to_date against something that is already a DATE.
By doing so, you force Oracle to do an implicit conversion to a string, which it will do so by using your NLS_DATE_FORMAT parameter to decide what format to output the string as, before it then tries to convert it back to a date using the format mask you specified, like so:
to_date(to_char(od.date2, '<nls_date_format parameter>'), 'DD/MM/YYYY')
If the two format masks aren't the same, you will run into errors... such as the one that prompted you to post this question!
I have string of date from xml file of such kind: '2010-09-09T22:33:44.OZ'
I need to extract only date and time. I want to ignore symbol T and .OZ (time zone). Which mask I should use? Thanks in advance
select TO_DATE('2010-09-09T22:33:44.OZ'
,'YYYY-MM-DD"T"HH24:MI:SS".OZ"')
from dual;
9/09/2010 10:33:44 PM
If the timezone information is needed:
select to_timestamp_tz('2010-09-09T22:33:44.GMT','YYYY-MM-DD"T"HH24:MI:SS.TZR')
from dual;
09-SEP-10 22.33.44.000000000 GMT
But OZ isn't a recognised timezone abbreviation, so you'd need to do some pre-conversion of that to something that is.
If you want to just ignore that part, and it's fixed, you can do what #Jeffrey Kemp said:
select to_date('2010-09-09T22:33:44.OZ','YYYY-MM-DD"T"HH24:MI:SS."OZ"')
from dual;
09/09/2010 22:33:44 -- assuming your NLS_DATE_FORMAT is DD/MM/YYYY HH24:MI:SS
If you want to ignore it but it isn't fixed then you'll need to trim it off first, something like (using a bind variable here for brevity):
var input varchar2(32);
exec :input := '2010-09-09T22:33:44.OZ';
select to_date(substr(:input,1,instr(:input,'.') - 1),'YYYY-MM-DD"T"HH24:MI:SS')
from dual;
09/09/2010 22:33:44