I am trying to update several dates to the end of the month. However, my table has two separate date formats ('DD-MON-YY' and 'YYYYMMDD'). How can I update both dates in an update statement? Also, I want the new dates to be in 'YYYYMMDD' format.
Update MY_TABLE
set MY_DATE = TO_CHAR(LAST_DAY(TO_DATE(MY_DATE,'DD-MON-YY')),'YYYYMMDD');
As David noted in the comments, the "real" solution here would be to add a date column and use that. If that's not an option, you could differentiate old data from new data based on the existence of the - character:
UPDATE my_table
SET my_date =
TO_CHAR(LAST_DAY(TO_DATE(my_date, CASE WHEN my_date LIKE '%-%'
THEN 'DD-MON-YY'
ELSE 'YYYYMMDD'
END)),
'YYYYMMDD')
I currently have a dataset with varying date entries (and a mixture of string entries) for which I need to parse. There are a few: 'M/DD/YY', 'M/D/YY', 'MM/DD/YY', 'MM/D/YY', 'MM/DD/YYYY'...). I could use some support with improving my regex to handle the varying formats and possible text entered in the date field.
My current Postgres query breaks out other entries into another column and reformats the date. Although, I've increased the year to 4 digits rather than 2, I believe the issue may live somewhere in the 'YYYY-MM-DD' formatting or that my query does not properly accommodate additional formatting within.
CASE WHEN date ~ '^\\\\d{1,2}/\\\\d{1,2}/\\\\d{4}$' THEN TO_DATE(date::date, 'YYYY-MM-DD')
ELSE NULL END AS x_date,
CASE WHEN NOT date ~ '^\\\\d{1,2}/\\\\d{1,2}/\\\\d{4}$' AND date <> '' THEN date
ELSE NULL END AS x_date_text
For the various date formats, they should be reformatted accordingly and for other non-date values, they should be moved over to the other column.
Based on your list of formats, I believe that just two regexes should be enough to check the values:
'^[0-9]{1,2}/[0-9]{1,2}/[0-9]{4}/$' would map to date format 'MM/DD/YYYY'
'^[0-9]{1,2}/[0-9]{1,2}/[0-9]{2}/$' would map to 'MM/DD/YY'
You can use a CASE construct to check the value against the regex and apply the proper mask when using TO_DATE().
However, since you need to split the data over two columns, you would need to tediously repeat the CASE expression twice, one for each column.
One way to simplify the solution (and to make it easier to maintain afterwards) would be to use a CTE to list the regexes and the associated date format. You can LEFT JOIN the CTE with the table.
Consider the following query:
WITH vars AS (
SELECT '^[0-9]{1,2}/[0-9]{1,2}/[0-9]{4}/$' reg, 'MM/DD/YYYY' format
UNION ALL '^[0-9]{1,2}/[0-9]{1,2}/[0-9]{2}/$', 'MM/DD/YY'
)
SELECT
CASE WHEN vars.reg IS NOT NULL THEN TO_DATE(t.date, vars.format) END x_date,
CASE WHEN vars.reg IS NULL THEN t.date END x_date_text
FROM
mytable t
LEFT JOIN vars ON t.date ~ vars.reg
If more regex/format pairs are needed, you just have to expand the CTE. Just pay attention to the fact that regexes should be exclusives (ie two different regexes should not possibly match on a single value), else you will get duplicated records in the result.
While the regex by #GMB insures format validity it passes many invalid dates, and with liberal to_date conversion by Postgres could introduce errors and or confusion. Run the following to see the liberal conversion:
set datestyle = 'ISO';
select dd,'01/' || dd || '/2019' mmddyyyy, to_date ( '01/' || dd || '/2019', 'mm/dd/yyyy')
from ( select generate_series( 0,40)::text dd) d;
select mm , mm ||'/01/2019' mmddyyyy, to_date ( mm ||'01/2019', 'mm/dd/yyyy')
from ( select generate_series( 0,40)::text mm) d;
If that liberal date conversion is acceptable - Great. But if not we can tighten it down considerable (although still not 100% valid results). Lets break the format down:
for date formats mm/dd/yyyy or mm/dd/yy
breakdown MM valid 1 - 12
valid character 0 followed by 1-9
1 followed by 0-2
regex (0?[1-9]|1[0-2)
DD valid 0 - 31 (sort of)
day 31 valid for April, June, Sep, Nov also evaluate valid but become
day 1 of May, July, Oct, Dec respectivally
days 29-31 of Feb also eveluate valid but become day
1-3 of march and 1-2 in lead yearsin non-leap years
valid character optional 0 followed by 1-9
1-2 followed by 0-9
3 followed by 0-1
regex (0?[1-9]|[1-2][0-9]|3[0-2])
YEAR valid 1900 - 2999 (no ancient history)
valid character 1-2 followed by 0-9,0-9,0-9
0-9,0-9
Now putting that together we get.
-- setup
drop table if exists my_dates;
create table my_dates(test_date text, status text);
insert into my_dates (test_date, status)
values ('01/15/2019', 'valid')
, ('12/25/0001', 'invalid year < 1900')
, ('12/01/2020', 'valid')
, ('oops', 'yea a date NOT')
, ('6/3/19', 'valid')
, ('2/29/2019', 'valid sort of, Postgres liberal evaluation of to_date')
, ('2/30/2019', 'valid sort of, Postgres liberal evaluation of to_date')
, ('2/31/2019', 'valid sort of, Postgres liberal evaluation of to_date')
, ('2/29/2020', 'valid')
, ('14/29/2020', 'invalid month 14')
, ('01/32/2019', 'invalid day 32')
, ('04/31/2019', 'valid sort of, Postgres liberal evaluation of to_date')
;
-- as query
set datestyle = 'ISO';
with patterns (pat, fmt) as (values ('^(0?[1-9]|1[0-2])/(0?[1-9]|[1-2][0-9]|3[0-1])/[12][0-9]{3}$'::text, 'mm/dd/yyyy')
, ('^(0?[1-9]|1[0-2])/(0?[1-9]|[1-2][0-9]|3[0-1])/[0-9]{2}$'::text, 'mm/dd/yy')
)
select to_date(test_date, fmt),status, test_date, pat, fmt
from my_dates
left join patterns on test_date ~ pat;
------------------------------------------------------------------
-- function accessable from SQL
create or replace function parse_date(check_date_in text)
returns date
language sql
as $$
with patterns (pat, fmt) as (values ('^(0?[1-9]|1[0-2])/(0?[1-9]|[1-2][0-9]|3[0-1])/[12][0-9]{3}$'::text, 'mm/dd/yyyy')
, ('^(0?[1-9]|1[0-2])/(0?[1-9]|[1-2][0-9]|3[0-1])/[0-9]{2}$'::text, 'mm/dd/yy')
)
select to_date(check_date_in, fmt)
from patterns
where check_date_in ~ pat;
$$;
--- test function
select test_date, parse_date(test_date), status from my_dates;
-- use demo
select * from my_dates
where parse_date(test_date) >= date '2020-01-02';
How can we compare a date to a format in Oracle?
Something like this: if MyDate is on format DD MONTH YYYY THEN /....
elsif MyDate is on format YYYY-MONTH-DD Then...
EDIT: My dates are in varchar2 and i want to keep them that way. I want just to know how to write a regex that would reprensent for example 10 October 2010.
Is it possible ? If it is a regex how would its format be please
Echoing what was mentioned in the comments to your question, best practice would be to have an actual DATE type field instead of VARCHAR2, and if you needed specific display formats, store those in another field as a format pattern. That said, you can use REGEXP_LIKE to check the format using the patterns in the below example.
with dateinfo as (
select 1 as id, '2018-MARCH-10' as dtString from dual
union all
select 2 as id, '10 MARCH 2018' as dtString from dual )
select id, dtString,
case
when regexp_like(dtString, '^[0-9]{4}-.[a-zA-Z]{3,}-.[0-9]{1,2}$') then 'format1'
when regexp_like(dtString, '^[0-9]{1,2} [a-zA-Z]{3,} [0-9]{4}$') then 'format2'
else 'no format'
end as dtFormat
from dateinfo;
I have the following error, ORA-01841
(full) year must be between -4713 and +9999 and not be 0
The error is coming from the below case statement.
Any help on what's going on and how to fix?
SQL
SELECT
CASE
WHEN NVL(uap.us_pend_dt, act_d_dt) >= TO_CHAR(TO_DATE('".PENDING_DATE_CUTOFF."','YYYY-MM-DD'),'mm/dd/yyyy')
THEN NVL(uap.us_pend_dt, act_d_dt)
ELSE CASE WHEN NVL(act_d_dt, SYSDATE) <TO_CHAR(TO_DATE('".HIRE_DATE_CUTOFF."','YYYY-MM-DD'),'mm/dd/yyyy')
THEN act_d_dt
ELSE ua_dt
END
END AS h_DT
It looks like you are trying to convert a string ".PENDING_DATE_CUTOFF." to a date. I just did the following and got an identical error:
SELECT TO_DATE('".PENDING_DATE_CUTOFF."', 'YYYY-MM-DD') FROM dual;
Is .PENDING_DATE_CUTOFF. (with periods) the name of a column in your table? If so then omit the single quote characters, e.g.:
SELECT TO_DATE(".PENDING_DATE_CUTOFF.", 'YYYY-MM-DD') FROM dual;
Of course this will give an altogether different error [ORA-00904: ".PENDING_DATE_CUTOFF.": invalid identifier] if you run as is! So I think you might want something like the following (I'm assuming the other date columns are actual dates, and the cutoff columns are VARCHAR2 columns that are storing dates in the format YYYY-MM-DD:
SELECT CASE WHEN COALESCE(uap.us_pend_dt, act_d_dt) >= TO_DATE(".PENDING_DATE_CUTOFF.", 'YYYY-MM-DD') THEN COALESCE(uap.us_pend_dt, act_d_dt)
ELSE WHEN COALESCE(act_d_dt, SYSDATE) < (TO_DATE(".HIRE_DATE_CUTOFF.", 'YYYY-MM-DD') THEN act_d_dt
ELSE act_d_dt
END AS h_dT
FROM mytable;
Note that I also got rid of the extraneous CASE statement and converted the Oracle-specific NVL() function to the ANSI-standard COALESCE() function.
EDIT: In the event that your *_dt columns are strings and not dates, best to convert them to dates using TO_DATE() before comparing - that way you're comparing dates to dates.
Hope this helps.
Presumably, your date columns are stored as dates. So, do the comparison as dates not strings:
SELECT (CASE WHEN NVL(uap.us_pend_dt, act_d_dt) >= TO_DATE('".PENDING_DATE_CUTOFF."', 'YYYY-MM-DD')
THEN NVL(uap.us_pend_dt, act_d_dt)
WHEN NVL(act_d_dt, SYSDATE) < TO_DATE('".HIRE_DATE_CUTOFF."', 'YYYY-MM-DD')
THEN act_d_dt
ELSE ua_dt
END) AS h_DT
You also don't need the extra case expressions.
I did a select in oracle database to return the time that ticket is in a support group.
Sometimes, I have the scenario where we have the date of ticket joined the group, but I'm not out of time.
To workaround this problem, I put in my select a condition and worked with diff between the date of entry in the group and the sysdate.
The problem is the output that is being formatted as follows: +00 00:28:32.00000, and I need only the time in minutes.
Below, I added the whole query, I suppose that the problem is in this part:
CASE
WHEN PBTI_TEMPONOGRUPO IS NULL
THEN (SYSDATE-(SECS_TO_DATE(PBTI_DATAENTRADAGRUPO)))
END AS TEMPO_NO_GRUPO
How to format this output?
The query is:
SELECT PBTI_WORKORDER_ID AS ID_WO,
PBTI_IDREQUISICAO AS ID_REQ,
PBTI_GRUPOSUPORTEATUAL AS GRUPO_SUP_ATUAL,
PBTI_GRUPOSUPORTEANTERIOR AS GRUPO_SUP_ANTERIOR,
CASE
WHEN PBTI_DATASAIDAGRUPO IS NULL
THEN SYSDATE
WHEN PBTI_DATASAIDAGRUPO IS NOT NULL
THEN SECS_TO_DATE(PBTI_DATASAIDAGRUPO)
END AS DATA_SAIDA_GRUPO,
SECS_TO_DATE(PBTI_DATAENTRADAGRUPO) AS DATA_ENTRADA,
CASE
WHEN PBTI_TEMPONOGRUPO IS NULL
THEN (SYSDATE-(SECS_TO_DATE(PBTI_DATAENTRADAGRUPO)))
END AS TEMPO_NO_GRUPO
FROM PBTI_TABELA_INDICADORES
WHERE PBTI_WORKORDER_ID = 'WO0000000142585';
CASE
WHEN PBTI_DATASAIDAGRUPO IS NULL
THEN SYSDATE
WHEN PBTI_DATASAIDAGRUPO IS NOT NULL
THEN SECS_TO_DATE(PBTI_DATASAIDAGRUPO)
END
So the output of the CASE expressions will be DATE data type. To get the output in your desired format, use TO_CHAR along with desired FORMAT MODEL to convert it into a string.
For example,
SQL> SELECT TO_CHAR(SYSDATE, 'YYYY-MM-DD') only_date FROM DUAL;
ONLY_DATE
----------
2015-10-20
SQL> SELECT TO_CHAR(SYSDATE, 'HH24:MI:SS') only_time FROM DUAL;
ONLY_TIME
---------
20:29:54