I want to migrate a table which contains some columns with dates. The issue is my dates are often in dd/mm/yyyyy HH24:MM:YYYY format. But sometimes it appears that the format is only dd/mm/yyyy, or blank.
I guess that's why I'm getting ORA-01830 when I'm trying to migrate the datas.
I tried
CASE
WHEN TO_DATE(MYDATE,'DD/MM/YYYY')
then TO_DATE(MYDATE,'DD/MM/YYYY 00:00:00')
END AS MYDATE
But I'm not sure if it is possible to test the date format (and ofcourse it's not working).
Thank you
TO_DATE cannot test date format, but you can do it. If Lalit's answer would not be enough, try something like
select
case when my_date like '__/__/__' then to_date(my_date, 'dd/mm/yy')
when my_date like '__-__-__' then to_date(my_date, 'dd-mm-yy')
...
end
So you have the data type issue. DATE is stored as string literal. As you have mentioned that the date model has the DD/MM/YYYY part same, just that the time portion is either missing for some rows or the entire value is NULL.
For example, let's say your table have the values like -
SQL> WITH dates AS(
2 SELECT 1 num, '29/12/2014 16:38:57' dt FROM dual UNION ALL
3 SELECT 2, '29/12/2014' FROM dual UNION ALL
4 SELECT 3, NULL FROM dual
5 )
6 SELECT num, dt
7 FROM dates
8 /
NUM DT
---------- -------------------
1 29/12/2014 16:38:57
2 29/12/2014
3
SQL>
TO_DATE with proper format model should do the trick.
Let's stick to a format model first.
SQL> alter session set nls_date_format='dd/mm/yyyy hh24:mi:ss';
Session altered.
Now, let's use TO_DATE to explicitly convert the string literal to date.
SQL> WITH dates AS(
2 SELECT 1 num, '29/12/2014 16:38:57' dt FROM dual UNION ALL
3 SELECT 2, '29/12/2014' FROM dual UNION ALL
4 SELECT 3, NULL FROM dual
5 )
6 SELECT num, to_date(dt, 'dd/mm/yyyy hh24:mi:ss') dt
7 FROM dates
8 /
NUM DT
---------- -------------------
1 29/12/2014 16:38:57
2 29/12/2014 00:00:00
3
SQL>
Related
I've inherited some data from an external source which is a timestamp. This was put into warehouse by someone as a varchar2. I need to convert this to a legitimate timestamp but am unsure how. This is how the string looks. "2021-04-23T11:02:17.00Z".
Would appreciate some help.
PS Ideally, I'd also like to know how to trunc this to a more traditional date format of DD-MMM-YYYY e.g. 21-Jan-2021 or even DD-MM-YYYY is fine.
Use to_timestamp_tz() to get the corresponding timstamp with time zone, convert it to the timezone you want it in (for example sessiontimezone) with AT TIME ZONE and cast() that to a timestamp.
SELECT cast(to_timestamp_tz('2021-04-23T11:02:17.00Z', 'YYYY-MM-DD"T"HH24:MI:SS.FF2:TZR') AT TIME ZONE sessiontimezone AS timestamp)
FROM dual;
db<>fiddle
Shouldn't be too difficult. Extract the "date" part, apply TO_DATE function to it (with appropriate format mask) and - that's all. It means that you should "stop" at the date_value in the following query. The last, final_result is a string again, just formatted as you wanted.
SQL> with test (col) as
2 (select '2021-04-23T11:02:17.00Z' from dual)
3 select substr(col, 1, 10) string,
4 --
5 to_date(substr(col, 1, 10), 'yyyy-mm-dd') date_value,
6 --
7 to_char(to_date(substr(col, 1, 10), 'yyyy-mm-dd'), 'dd-mm-yyyy') final_result
8 from test;
STRING DATE_VALUE FINAL_RESU
---------- ---------- ----------
2021-04-23 2021-04-23 23-04-2021
SQL>
In order to avoid that "operation", you might even create a view. For example:
This is a table you currently have:
SQL> create table test as
2 (select 1 id, 'Littlefoot' name, '2021-04-23T11:02:17.00Z' col from dual);
Table created.
Create a view, re-using code I posted above:
SQL> create or replace view v_test as
2 select id, name,
3 to_date(substr(col, 1, 10), 'yyyy-mm-dd') col
4 from test;
View created.
Select from it:
SQL> select * from v_test;
ID NAME COL
---------- ---------- ----------
1 Littlefoot 2021-04-23
Want another format? No problem:
SQL> alter session set nls_date_format = 'dd-mon-yyyy';
Session altered.
SQL> select * from v_test;
ID NAME COL
---------- ---------- -----------
1 Littlefoot 23-apr-2021
SQL>
Or, apply TO_CHAR to view's col column (also demonstrated in code I posted first; see the final_result).
You can use the TO_TIMESTAMP function to do this.
Try:
TO_TIMESTAMP('2021-04-23T11:02:17.00Z', 'YYYY-MM-DDTHH:MI:SS')
You can read more about this function in their Documentation
I have a table abc as:
-- start_time |end_time | total_time_taken
-- 27.05.2020 00:52:48 |27.05.2020 02:08:33 |
I want to set the value of total_time_taken as the difference of end_time-start_time. in the format "HH:MM:SS".I searched the similar topic but didnot find the exact answer.
My expected output is like : 01:44:12 (HH:MM:SS)
So,i tried :
SELECT To_Char(end_time,'HH24:MM:SS'),To_Char(start_time,'HH24:MM:SS'),
To_Char(end_time,'HH24:MM:SS')-To_Char(start_time,'HH24:MM:SS') FROM abc;
The datatypes of start_time,end_time,total_time_taken is DATE.Please help me to find the solution.
If you cast those dates as timestamps, you can easily subtract them and see relatively nice result:
SQL> with test (st, et) as
2 (select to_date('27.05.2020 00:52:48', 'dd.mm.yyyy hh24:mi:ss'),
3 to_date('27.05.2020 02:08:33', 'dd.mm.yyyy hh24:mi:ss')
4 from dual
5 )
6 select cast(et as timestamp) - cast(st as timestamp) diff
7 from test;
DIFF
--------------------------------------------------------------------------
+000000000 01:15:45.000000
SQL>
If you want to format it as you wanted (note that mm format mask is for months; mi is for minutes), then you could do some extracting - again from timestamp (won't work for date):
SQL> with test (st, et) as
2 (select to_date('27.05.2020 00:52:48', 'dd.mm.yyyy hh24:mi:ss'),
3 to_date('27.05.2020 02:08:33', 'dd.mm.yyyy hh24:mi:ss')
4 from dual
5 ),
6 diff as
7 (select cast(et as timestamp) - cast(st as timestamp) diff
8 from test
9 )
10 select extract(hour from diff) ||':'||
11 extract(minute from diff) ||':'||
12 extract(second from diff) diff
13 from diff;
DIFF
-------------------------------------------------------------------------
1:15:45
SQL>
You can further make it pretty (e.g. two digits for hours, using LPAD function). Or, you can even write your own function which will actually work on difference of DATE datatype values, do some calculations (using trunc function, subtractions, whatnot), but the above looks pretty elegant if compared to a home-made function.
The answer by Littlefoot is perfectly fine. This answer is just to show there is more than one way to get the result.
First, we can subtract one date from another and get the difference in days, then convert that difference to an interval.
with test (st, et) as
(select to_date('27.05.2020 00:52:48', 'dd.mm.yyyy hh24:mi:ss'),
to_date('27.05.2020 02:08:33', 'dd.mm.yyyy hh24:mi:ss')
from dual
)
select numtodsinterval(et-st, 'day') diff
from test;
Then, since we can't control interval formatting directly, we can add DIFF to an arbitrary date and then use built-in date formatting.
with test (st, et) as
(select to_date('27.05.2020 00:52:48', 'dd.mm.yyyy hh24:mi:ss'),
to_date('27.05.2020 02:08:33', 'dd.mm.yyyy hh24:mi:ss')
from dual
)
select to_char(date '1-1-1' + numtodsinterval(et-st, 'day'), 'hh24:mi:ss') diff
from test;
DIFF
--------
01:15:45
In oracle in our project where DECODE function is the recommended guideline to use them, there are issues when converting the date 01-01-1900.
Consider the below decode
CAST(DECODE(COL1,'A',DATECOL,TO_DATE('01011900','DDMMYYYY')) AS DATE) - (The CAST is also a mandatory guideline)
If,
COL1 DATECOL
A 2019-04-05
B 2018-01-01
C 2020-05-01
I get the resulting data as
COL1 OUTPUT
A 2019-04-05
B 2000-01-01
C 2000-01-01
The DECODE function gives me 2000-01-01 instead of 1900-01-01.
I have checked out the NLS parameters and dabbles around by modifying different things in TO_DATE function. Nothing worked out.
Is it possible that your DATECOL in the database is actually a string? Because if that is the case, then DECODE always takes the first potential return item to set the datatype. And once you start mixing and matching datatypes, bad things can be happen.
For example
SQL> alter session set nls_date_format = 'YY-MM-DD';
Session altered.
SQL> with t as
2 ( select 'A' col1, '2010-01-01' datecol from dual union all
3 select 'B' col1, '2010-01-01' datecol from dual )
4 select CAST(DECODE(COL1,'A',DATECOL,TO_DATE('01011900','DDMMYYYY')) AS DATE)
5 from t;
CAST(DEC
--------
10-01-01
00-01-01
That seems to look OK, but if I dig into the value that actually came back after all the jumping through datatypes
SQL> with t as
2 ( select 'A' col1, '2010-01-01' datecol from dual union all
3 select 'B' col1, '2010-01-01' datecol from dual )
4 select to_char(CAST(DECODE(COL1,'A',DATECOL,TO_DATE('01011900','DDMMYYYY')) AS DATE),'dd-mm-yyyy')
5 from t;
TO_CHAR(CA
----------
01-01-2010
01-01-2000
you can see that the lack of a full century specification has "mangled" the result. In particular, the tools you are using (SQL Developer and others) will have their own formatting of results which can mask what might be happening with datatypes.
Hi I am working on oracle DB. The DB has one column date. It consists of the dates of 5 years with the dates model will be refreshed. Ex
DATE_TABLE
DATE
------------
1-jan-2013
15-jan-2013
31-jan-2013
6-feb-2013
etc.........
now for today's date suppose 13th jan 2013. The next refresh date will be 15th jan. and previous refresh date is 1st jan. to retrieve these two dates. Can i have any way without using PL/SQL. using regular select queries?. Thanks in advance
There are two functions LAG() (allows you to reference previous record) and LEAD() allows you to reference next record. Here is an example:
SQL> with t1(col) as(
2 select '1-jan-2013' from dual union all
3 select '15-jan-2013' from dual union all
4 select '31-jan-2013' from dual union all
5 select '6-feb-2013' from dual
6 )
7 select col as current_value
8 , lag(col, 1) over(order by col) as prev_value
9 , lead(col, 1) over(order by col)as next_value
10 from t1
11 ;
Result:
CURRENT_VALUE PREV_VALUE NEXT_VALUE
------------- ----------- -----------
1-jan-2013 NULL 15-jan-2013
15-jan-2013 1-jan-2013 31-jan-2013
31-jan-2013 15-jan-2013 6-feb-2013
6-feb-2013 31-jan-2013 NULL
We can simply use the below query, plain and simple. No need of pl/sql
SELECT MIN(DATE) FROM DATE_TABLE WHERE DATE > SYSDATE ;
Data in the file_name field of the generation table should be an assigned number, then _01, _02, or _03, etc. and then .pdf (example 82617_01.pdf).
Somewhere, the program is putting a state name and sometimes a date/time stamp, between the assigned number and the 01, 02, etc. (82617_ALABAMA_01.pdf or 19998_MAINE_07-31-2010_11-05-59_AM.pdf or 5485325_OREGON_01.pdf for example).
We would like to develop a SQL statement to find the bad file names and fix them. In theory it seems rather simple to find file names that include a varchar2 data type and remove it, but putting the statement together is beyond me.
Any help or suggestions appreciated.
Something like:
UPDATE GENERATION
SET FILE_NAME (?)
WHERE FILE_NAME (?...LIKE '%STRING%');?
You can find the problem rows like this:
select *
from Files
where length(FILE_NAME) - length(replace(FILE_NAME, '_', '')) > 1
You can fix them like this:
update Files
set FILE_NAME = SUBSTR(FILE_NAME, 1, instr(FILE_NAME, '_') -1) ||
SUBSTR(FILE_NAME, instr(FILE_NAME, '_', 1, 2))
where length(FILE_NAME) - length(replace(FILE_NAME, '_', '')) > 1
SQL Fiddle Example
You can also use Regexp_replace function:
SQL> with t1(col) as(
2 select '82617_mm_01.pdf' from dual union all
3 select '456546_khkjh_89kjh_67_01.pdf' from dual union all
4 select '19998_MAINE_07-31-2010_11-05-59_AM.pdf' from dual union all
5 select '5485325_OREGON_01.pdf' from dual
6 )
7 select col
8 , regexp_replace(col, '^([0-9]+)_(.*)_(\d{2}\.pdf)$', '\1_\3') res
9 from t1;
COL RES
-------------------------------------- -----------------------------------------
82617_mm_01.pdf 82617_01.pdf
456546_khkjh_89kjh_67_01.pdf 456546_01.pdf
19998_MAINE_07-31-2010_11-05-59_AM.pdf 19998_MAINE_07-31-2010_11-05-59_AM.pdf
5485325_OREGON_01.pdf 5485325_01.pdf
To display good or bad data regexp_like function will come in handy:
SQL> with t1(col) as(
2 select '826170_01.pdf' from dual union all
3 select '456546_01.pdf' from dual union all
4 select '19998_MAINE_07-31-2010_11-05-59_AM.pdf' from dual union all
5 select '5485325_OREGON_01.pdf' from dual
6 )
7 select col bad_data
8 from t1
9 where not regexp_like(col, '^[0-9]+_\d{2}\.pdf$');
BAD_DATA
--------------------------------------
19998_MAINE_07-31-2010_11-05-59_AM.pdf
5485325_OREGON_01.pdf
SQL> with t1(col) as(
2 select '826170_01.pdf' from dual union all
3 select '456546_01.pdf' from dual union all
4 select '19998_MAINE_07-31-2010_11-05-59_AM.pdf' from dual union all
5 select '5485325_OREGON_01.pdf' from dual
6 )
7 select col good_data
8 from t1
9 where regexp_like(col, '^[0-9]+_\d{2}\.pdf$');
GOOD_DATA
--------------------------------------
826170_01.pdf
456546_01.pdf
To that end your update statement might look like this:
update your_table
set col = regexp_replace(col, '^([0-9]+)_(.*)_(\d{2}\.pdf)$', '\1_\3');
--where clause if needed