I have a file that inputs data in the format m/d/yyyy or m/dd/yyyy depending on whether or not the date value is 2 digits (example 4/1/2015 or 4/14/2015). I need to convert this to a date with the format yyyy/mm/dd (example 2015/04/01 or 2015/04/14). I have tried multiple way but I get the error "Invalid date" every time. Please help.
Things I have tried:
cast((CASE WHEN CHAR_LENGTH(RSA_dt) <10 THEN 0 || TRIM(RSA_dt) end) AS DATE)
CAST( CAST( RSA_dt AS DATE FORMAT 'DD-MMM-YY') AS DATE FORMAT 'YYYY-MM-DD')
cast(RSA_dt as date format 'YYYY-MM-DD')
Which tool do you use for loading?
The easiest way to load this data is to define that column as a VARDATE in a TPT job:
VARDATE(10) FORMATIN 'MM/DD/YY' FORMATOUT 'YYYY-MM-DD'
Otherwise you got a problem as Teradata's CAST doesn't like single digit day/month. Starting with TD14 there's Oracle's TO_DATE, which still doesn't like a single digit month, but at least tolerates single digit day:
TO_DATE(CASE
WHEN RSA_dt LIKE '_/%'
THEN '0' || RSA_dt
ELSE RSA_dt
END
,'mm/dd/yyyy')
This will work for TPT.
Select '1/1/2014' as date1,
TO_DATE (
case
when strtok(date1, '/', 1) between 1 and 9 and strtok(date1, '/', 2) between 1 and 9 then strtok(date1,'/', 3)||'/0'||strtok(date1, '/', 1)||'/0'||strtok(date1,'/', 2)
when strtok(date1, '/', 1) between 1 and 9 and strtok(date1, '/', 2) > 9 then strtok(date1,'/', 3)||'/0'||strtok(date1, '/', 1)||'/'||strtok(date1,'/', 2)
when strtok(date1, '/', 1) > 9 and strtok(date1, '/', 2) between 1 and 9 then strtok(date1,'/', 3)||'/'||strtok(date1, '/', 1)||'/0'||strtok(date1,'/', 2)
else strtok(date1, '/', 3)||'/'||strtok(date1, '/', 1)||'/'||strtok(date1,'/', 2)
end , 'YYYY/MM/DD') as req_date
Here's another solution. It uses a regular expression to add a leading zero to any single-digit number, then standard casting to convert it to a date using existing format, then back to standard format. I have included several examples:
SELECT CAST(CAST(TD_SYSFNLIB.REGEXP_REPLACE('4/1/2015','(?<!\d)(\d)(?!\d)','0\1',1,0,'i') AS DATE FORMAT 'MM/DD/YYYY') AS DATE FORMAT 'YYYY-MM-DD');
SELECT CAST(CAST(TD_SYSFNLIB.REGEXP_REPLACE('4/14/2015','(?<!\d)(\d)(?!\d)','0\1',1,0,'i') AS DATE FORMAT 'MM/DD/YYYY') AS DATE FORMAT 'YYYY-MM-DD');
SELECT CAST(CAST(TD_SYSFNLIB.REGEXP_REPLACE('12/1/2015','(?<!\d)(\d)(?!\d)','0\1',1,0,'i') AS DATE FORMAT 'MM/DD/YYYY') AS DATE FORMAT 'YYYY-MM-DD');
It looks like you're providing the format you want, instead of the format you've got. Try this instead:
cast(RSA_dt as date format 'MM/DD/YYYY')
If you aren't using TPT, then you're stuck playing some awful substring games.
SELECT
SUBSTR(chardate,INSTR(chardate,'/',1,2)+ 1,4) AS theYear,
'00' || SUBSTR(chardate,1,INSTR(chardate,'1',1,1)-2) AS theMonth,
'00' || SUBSTR(chardate,INSTR(chardate,'/',1,1)+ 1,INSTR(chardate,'/',1,2) - INSTR(chardate,'/',1,1)-1) AS theDate,
CAST (theYear || '-' || SUBSTRING(theMonth,LENGTH(theMonth) -1,2) || '-' || SUBSTR(theDate,LENGTH(thedate)-1,2) AS DATE) AS ItsADate
FROM
<yourtable>
Really ugly, but it should work.
This should work.
Select '1/2/2014' as date1,
TO_CHAR(TO_DATE (
case
when strtok(date1, '/', 2) between 1 and 9 then strtok(date1,'/', 3)||'/0'||strtok(date1, '/', 1)||'/'||strtok(date1,'/', 2)
else strtok(date1, '/', 3)||'/'||strtok(date1, '/', 1)||'/'||strtok(date1,'/', 2)
end , 'YYYY/MM/DD'), 'YYYY/MM/DD') as "YYYY/MM/DD"
This will work, though not pretty. Specifically converting M/D/YYYY to YYYYMMDD
select substr(YOURDATE,-4,4) ||
substr('00'||SUBSTR(YOURDATE,1,to_number(regexp_instr(YOURDATE,'\/'))-1),-2,2) ||
case when substr(YOURDATE,2,1) = '/' then
case when substr(YOURDATE,4,1) = '/' then
'0' || substr(YOURDATE,3,1)
else substr(YOURDATE,3,2)
end
else case when substr(YOURDATE,5,1) = '/' then
'0' || substr(YOURDATE,4,1)
else substr(YOURDATE,4,2)
end
end as NEWDATE
from YOURTABLE
Related
I want to convert a VARCHAR2-value like '-28:15:00' to INTERVAL.
With a literal value, this works:
select interval '-09:11:36' hour to second from dual;
However, this does not (ORA-00923: FROM keyword not found where expected):
select interval MY_VARCHAR hour to second from MY_TABLE;
--comparable to select interval to_char(sysdate, 'hh:mm:ss') hour to second from dual;
My assumption is that the literal value is implicitly cast while the explicit varchar-value from MY_VARCHAR (or char from to_char respectively) is not valid between "interval" and "hour".
CAST like this does not work (ORA-00963: unsupported interval type):
select cast(MY_VARCHAR as interval hour to second) from MY_TABLE;
--comparable to select cast('09:11:36' as interval hour to second) from dual;
What does work is concatenating '0 ' as the day-value and cast it to INTERVAL DAY TO SECOND:
select cast('0 ' || '09:11:36' as interval day to second) from dual;
However this only works for positive values, and as long as the value for hour is below 24.
Is there a better solution than dissecting the VARCHAR-value with CASE, SUBSTR and so on?
You need the minus sign before the days to cast it to an interval:
SELECT value,
CAST( REGEXP_REPLACE(value, '^(-)?', '\10 ') AS INTERVAL DAY TO SECOND )
AS interval_value
FROM table_name
or, using simple string functions, which slightly more to type but probably more efficient (as regular expressions are slow):
SELECT value,
CAST(
CASE
WHEN value LIKE '-%'
THEN '-0 ' || SUBSTR(value, 2)
ELSE '0 ' || value
END
AS INTERVAL DAY TO SECOND
) AS interval_value
FROM table_name
Which, for the sample data:
CREATE TABLE table_name (value) AS
SELECT '-09:11:36' FROM DUAL UNION ALL
SELECT '09:11:36' FROM DUAL;
Both output:
VALUE
INTERVAL_VALUE
-09:11:36
-00 09:11:36.000000
09:11:36
+00 09:11:36.000000
fiddle
Another approach, which would accept hour values greater than 23, is to coerce the string into ISO format and use the to_dsinterval() function:
select my_varchar,
to_dsinterval(
regexp_replace(my_varchar, '(-?)(\d+):(\d+):(\d+)', '\1PT\2H\3M\4S')
) as interval_value
from my_table
MY_VARCHAR
INTERVAL_VALUE
-09:11:36
-000000000 09:11:36.000000000
09:11:36
+000000000 09:11:36.000000000
28:15:02
+000000001 04:15:02.000000000
-28:15:02
-000000001 04:15:02.000000000
fiddle
You could do reformatting of the string with string functions, which as #MTO mentioned will be faster for large amounts of data, but it would be a bit messy:
select my_varchar,
to_dsinterval(
case
when substr(my_varchar, 1, 1) = '-'
then '-PT' || substr(my_varchar, 2, instr(my_varchar, ':', 1, 1) - 2)
else 'PT' || substr(my_varchar, 1, instr(my_varchar, ':', 1, 1) - 1)
end || 'H'
|| substr(my_varchar, instr(my_varchar, ':', 1, 1) + 1,
instr(my_varchar, ':', 1, 2) - instr(my_varchar, ':', 1, 1) - 1) || 'M'
|| substr(my_varchar, instr(my_varchar, ':', 1, 2) + 1) || 'S'
) as interval_value
from my_table
MY_VARCHAR
INTERVAL_VALUE
-09:11:36
-000000000 09:11:36.000000000
09:11:36
+000000000 09:11:36.000000000
28:15:02
+000000001 04:15:02.000000000
-28:15:02
-000000001 04:15:02.000000000
fiddle
As shown in the documentation, interval literals (like all literals, as the name implies) require a fixed value, not an expression or column; the quotes are not optional in the railroad diagram:
... so your first query gets ORA-00923.
And the cast() function only supports certain conversions:
Note 1: Datetime/interval includes DATE, TIMESTAMP, TIMESTAMP WITH TIMEZONE, TIMESTAMP WITH LOCAL TIME ZONE, INTERVAL DAY TO SECOND, and INTERVAL YEAR TO MONTH.
so you can't cast(... as interval hour to second), and that will throw ORA-00963.
I have 1 column that displays year number in the format 1999 and I have another column that displays month number as 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12.
How do I get the single months to display with a 0 in front? I need to combine these 2 columns to display in the form of yyyy/mm so it will be 1999/01 for January 1999.
I tried:
SELECT
YearNumber + '/' + FORMAT(MonthNumber, 'mm') AS PaymentMonth
But I get this error:
Conversion failed when converting the varchar value '/' to data type smallint
Please try:
SELECT
CAST(YearNumber AS varchar(4)) + '/' +
LEFT('0' + CAST(MonthNumber AS varchar(2)), 2) AS PaymentMonth;
Another option, using case when:
The table:
select * from mytable
# YearNumber MonthNumber
# 1999 2
# 2000 11
select YearNumber || '/' ||
(case when MonthNumber < 10 then '0' else '' end) ||
MonthNumber as YearMonth
from mytable
# YearMonth
# 1999/02
# 2000/11
Note: the above works in sqlite, which tends to be more permissive with column types. In SQL Server, if the columns are not strings already then you may need to cast(YearNumber as char(4)) or perhaps use the concat function:
select
concat(YearNumber, '/',
(case when MonthNumber < 10 then '0' else '' end),
MonthNumber) as YearMonth
from mytable
Other DBMSes have different dialects, they may differ slightly.
SQL Date Format with the FORMAT function
Use the FORMAT function to format the date and time data types from a date column (date, datetime, datetime2, smalldatetime, datetimeoffset, etc. data type) in a table or a variable such as GETDATE()
To get DD/MM/YYYY use SELECT FORMAT (getdate(), 'dd/MM/yyyy ') as date
To get MM-DD-YY use SELECT FORMAT (getdate(), 'MM-dd-yy') as date.
You can use the concat() function to join them. Depending on the database you can use || instead.
select concat(col1, '/', col2) from tbl;
This is also standard, but not enabled by default on MySQL, and possibly other databases.
select col1 || '/' || col2 from tbl;
I have a data set that has dates like this:
MM DD YY
2 8 10
3 9 11
4 10 12
I'm trying to write a query that displays the dates in one single column as MM/DD/YY
Any help would be greatly appreciated thanks
If you dont mind the data format, you can concatenate them.
select MM || '/' || 'DD' || '/' || YY DATE_COL from T
you can also convert it to date
select to_Date(MM || '/' || 'DD' || '/' || YY, 'MM/DD/YY') DATE_COL from T
Teradata actually stores dates as integers, as explained in the documentation:
Teradata Database stores each DATE value as a four-byte integer using the following formula: (year - 1900) * 10000 + (month * 100) + day.
If you want to generate a result of date datatype from your numbers, you can just do:
cast((yy + 100) * 10000 + mm * 100 + dd as date)
On the other hand, if you just want a string in format mm/dd/yy, it is probably simpler to use string functions; we just need to pad the 1-digit values with 0:
lpad(mm, 2, '0') || '/' || lpad(dd, 2, '0') || '/' || lpad(yy, 2, '0')
You can do below, assuming your year is in the 21st century.
select to_Date(lpad(MM,2,'0')|| '/' ||lpad(DD,2,'0')|| '/20' ||lpad(YY,2,'0'),'MM/DD/YYYY') as Date_field
from my_Table;
Here is a demo in Oracle - https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=1ba6814d793f2b8b269611e348540f6c. This would work in Teradata as well.
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.
I have 2 INTEGER columns like the following:
Month Year
----- -----
5 2011
Is there any way to convert that to a single column VARCHAR like this: May-2011
I don't know of an easy way to do this since you don't have a date object (ie its not like youre finding the month of a timestamp), you can use a case statement but it gets long.
SELECT CASE Month
WHEN '1' THEN 'January'
WHEN '2' THEN 'February'
WHEN '3' THEN 'March'
WHEN '4' THEN 'April'
...
END+'-'+Year
FROM TABLE
I think this will do it:
SELECT
MONTHNAME(
DATE(CAST(Year AS CHAR(4)) || '-' || TRIM(CAST(Month AS CHAR(2))) || '-1')
) || '-' || CAST(Year AS CHAR(4))
FROM TABLE
This should do the trick, assuming that the columns Month and Year are integers and Month has the domain 1-12:
select substring('---JanFebMarAprMayJunJulAugSepOctNovDec', 3*Month , 3 )
+ '-'
+ right(digits(Year),4)
from some_table
If Month is 0 you'll get '---' as the month; if it's less than 0 or greater than 12, you'll get some sort of blooey.
You could create a function to convert the month value, like this...
CREATE FUNCTION INT2MONTH (MONTH INTEGER)
RETURNS VARCHAR(100)
LANGUAGE SQL
CONTAINS SQL
NO EXTERNAL ACTION
DETERMINISTIC
RETURN MONTHNAME('2000-' || RIGHT('0' || STRIP(CHAR(MONTH)), 2) || '-01')
Then you can...
select int2month(month) || '-' || strip(char(year)) from test
1
--------------------------------------------------
May-2011
June-2011
December-2012
If you want a 3 char month then change last last on function to...
RETURN LEFT(MONTHNAME('2000-' || RIGHT('0' || STRIP(CHAR(MONTH)), 2) || '-01'), 3)
I realize this question is pretty old, but there's a way that is a lot simpler than any of the options listed here (in my opinion) -- a combination of some date math and the VARCHAR_FORMAR() function:
SELECT
VARCHAR_FORMAT(
DATE('0001-01-01') + (month_col - 1) MONTH + (year_col - 1) YEAR
,'Month-YYYY'
)
FROM your_table