Convert two fields (Month and Year) into a YYYY-MM-DD Field - hana

I am using SAP HANA SQL (Through Alteryx) via an in-DB formula.
I have two fields (Month and YEAR) and I need to convert/combine these into one field shown as YYYY-MM-DD. I am able to do this succesfully locally in Alteryx but I need to make this happen within the DB via SQL.
See image for succesful local conversion in Alteryx:

There seem to be two goals here:
construct a valid date from year and month information.
represent this date in a specific format, ie. YYYY-MM-DD
The first part can be done in HANA like this:
to_date( "<year_column>" || "<month_column>", 'YYYYMM') as newDate
The double-pipe || operator concatenates strings, which means, that <year_column> and <month_column> data will be first converted into strings if these are not already string-values.
The concatenated string is then turned into a date data type. The to_date conversion function takes the pattern string YYYYMM and since the day information is missing, it makes it up on the fly and sets the day to the first day of the month.
This to_date conversion also checks for that only valid dates are created.
If, for example, the MM would not be a value between 01 and 12 then the conversion would fail with an error.
This brings me to the next potential obstacle to look out for: the conversion string pattern YYYYMM requires that there will be exactly four digits denoting the year and exactly two digits for the month.
While this may be fine for the existing year data as most dates are denoted with four digits nowadays, there is a good chance that the month data does not have a leading zero (e.g. when the data is currently stored in a numeric field).
To "fix" this issue, we can just add the leading zero for all values that only have a single digit so far. There's a couple of ways to do this in HANA, and as this does not seem to be in an ABAP context, I'd go with a way that works on most SQL databases:
LPAD ("<month_column>", 2, '0')
This gets us to the following expression for step 1:
to_date( "<year_column>" || LPAD ("<month_column>", 2, '0'), 'YYYYMM') as newDate
Step 2 now is relatively easy: turn the date-data that we constructed in step 1 and represent it in a specific format.
Since date-data per se does not have a specific output format (ie. you can display or print the same date format any way you like - it doesn't change the data), it needs to be converted to a string for that.
The conversion function for that is called TO_NVARCHAR() and can also take a conversion pattern:
to_nvarchar( "<date_data>", 'YYYY-MM-DD') as fixedFormatDate
is what we're looking for this question.
Putting it all together into a single expression:
to_nvarchar(to_date( "<year_column>"
|| LPAD ("<month_column>", 2, '0')
, 'YYYYMM')
, 'YYYY-MM-DD') as fixedFormatDate
While this is a long answer to a seemingly simple question, I believe it is important to understand all the involved steps that are necessary for this conversion.

Related

SQL Server Not Casting String YYYYMMdd to Date

I have a query that was working fine before a server migration and now is not working. I'm trying to convert all dates to a specific month and year, but I keep getting this error:
Conversion failed when converting date and/or time from character string.
Looking into the data, there are no null values in InputDate, which is a date data type column. When I run the Concat() function everything is formatted as 'YYYYMMdd', yet both CAST and CONVERT fail with the same error.
Is there an issue with my query that I'm not seeing?
SELECT RandoSTUFF,
DATEADD(day,2,CAST(CONCAT('2023','02',FORMAT(InputDate,'dd')) AS date)) AS MovedDate
FROM a_table_
I expect the issue is you have date values near the end of their months, and you're trying to compose the equivalent values for February, which is shorter.
So if you have an InputDate value of, say, 2022-12-31 and run the code in the question, it will extract the 31 and concat it with the other values, and you'll end up trying to do this:
CAST('20230231' as Date)
Of course, there is no such date.
As it is, it's not clear whether you want such an input to map to February 28 or March 3. To fix this, you'll need to rethink the problem so you only try to map to valid dates, and ensure the final result is more clearly defined. This is one of the many reasons it's almost always better to use Date/time functions instead of composing dates from strings.

Unable to get data between two years

I am not getting data between two years, below is between condition
to_char(Wfc.APPLYDTM,'MM/DD/YYYY') between '12/11/2019' and '01/10/2020'
but I am getting data between '12/11/2019' and '12/31/2019' & '01/11/2020' and '01/01/2020' for these dates but not between two different years.
Please help
Try using TO_DATE instead of TO_CHAR, and then compare against valid Oracle date literals:
SELECT *
FROM Wfc
WHERE TO_DATE(APPLYDTM, 'MM/DD/YYYY') BETWEEN date '2019-12-11' AND date '2019-01-10';
Note that if APPLYDTM already be a date, then you don't need to call TO_DATE on it. It doesn't make sense to convert your data to character, if you intend to work with it as a date.
You should convert your data to Date to be able to compare correctly.
The main idea is you should compare date value instead of string value.
to_date(Wfc.APPLYDTM,'MM/dd/yyyy') between to_date('12/11/2019','MM/dd/yyyy') and to_date('01/10/2020','MM/dd/yyyy')
Read here to more details.
Do not convert date/time values to strings! Use the built in functionality.
Your logic is most simply expressed as:
Wfc.APPLYDTMbetween >= DATE '2019-12-11' AND
Wfc.APPLYDTMbetween < DATE '2020-01-11'
Note that the date constants are provided using the DATE keyword. This supposed ISO 8601 standard date formats (happily!).
Also note the use of >= and < rather than BETWEEN. The date data type in Oracle can include a time component -- even if you don't see it when you query the table. This ensures that all date/times are included in the range.
As an added benefit, this can use an index on (APPLYDTMbetween). Using a function usually precludes using an index, unless you have defined a function-based index.

DB2 Convert Number to Date

For some reason (I have no control over this) dates are stored as Integers in an iSeries AS400 DB2 system that I need to query. E.g. today will be stored as:
20,171,221
Being in the UK I need it to be like the below in Date format:
21/12/2017
This is from my query: (OAORDT = date field)
Select
Date(SUBSTR( CHAR( OAORDT ),7,2) ||'/' || SUBSTR(CHAR ( OAORDT ),5,2) || '/' || SUBSTR(CHAR (OAORDT ),1,4)) AS "Order Date"
from some.table
However, all I get is Nulls. If I remove the Date function, then it does work but its now a string, which I don't want:
Select
SUBSTR( CHAR( OAORDT ),7,2) ||'/' || SUBSTR(CHAR ( OAORDT ),5,2) || '/' || SUBSTR(CHAR (OAORDT ),1,4) AS "Order Date"
from some.table
How do I convert the OAORDT field to Date?
Just to update - I will be querying this from MS SQL Server using an OpenQuery
Thanks.
1) How do I convert the OAORDT field to Date?
Simplest is to use TIMESTAMP_FORMAT :
SELECT DATE(TIMESTAMP_FORMAT(CHAR(OAORDT),'YYYYMMDD'))
2) Being in the UK I need it to be [...] in Date format 21/12/2017 :
SELECT VARCHAR_FORMAT(DATE(TIMESTAMP_FORMAT(CHAR(OAORDT),'YYYYMMDD')),'DD/MM/YYYY')
Note, you didn't specify where you are doing this, but since you tagged as ibm-midrange, I am answering for embedded SQL. If you want JDBC, or ODBC, or interactive SQL, the concept is similar, just the means of achieving it is different.
Make sure SQL is using dates in the correct format, it defaults to *ISO. For you it should be *EUR. In RPG, you can do it this way:
exec sql set option *datfmt = *EUR;
Make sure that set option is the first SQL statement in your program, I generally put it immediately between D and C specs.
Note that this is not an optimal solution for a program. Best practice is to set the RPG and SQL date formats both to *ISO. I like to do that explicitly. RPG date format is set by
ctl-opt DatFmt(*ISO);
SQL date format is set by
exec sql set option *datfmt = *ISO;
Now all internal dates are processed in *ISO format, and have no year range limitation (year can be 0001 - 9999). And you can display or print in any format you please. Likewise, you can receive input in any format you please.
Edit Dates are a unique beast. Not every language, nor OS knows how to handle them. If you are looking for a Date value, the only format you need to specify is the format of the string you are converting to a Date. You don't need to (can't) specify the internal format of the Date field, and the external format of a Date field can be mostly anything you want, and different each time you use it. So when you use TIMESTAMP_FORMAT() as #Stavr00 mentioned:
DATE(TIMESTAMP_FORMAT(CHAR(OAORDT),'YYYYMMDD'))
The format provided is not the format of the Date field, but the format of the data being converted to a Timestamp. Then the Date() function converts the Timestamp value into a Date value. At this point format doesn't matter because regardless of which external format you have specified by *DATFMT, the timestamp is in the internal timestamp format, and the date value is in the internal date format. The next time the format matters is when you present the Date value to a user as a string or number. At that point the format can be set to *ISO, *EUR, *USA, *JIS, *YMD, *MDY, *DMY, or *JUL, and in some cases *LONGJUL and the *Cxxx formats are available.
Since none of variants suited my needs I've came out with my own.
It is as simple as:
select * from yourschema.yourtable where yourdate = int(CURRENT DATE - 1 days) - 19000000;
This days thing is leap year-aware and suits most needs fine.
Same way days can be turned to months or years.
No need for heavy artillery like VARCHAR_FORMAT/TIMESTAMP_FORMAT.
Below worked for me:
select date(substring(trim(DateCharCol), 1, 2)||'/'||substring(trim(DateCharCol), 3, 2)||'/'||'20'||substring(trim(DateCharCol), 5, 2)) from yourTable where TableCol =?;

Validate a date comprised of 3 fields in PLSQL

I am not an expert in PLSQL. Normally to validate a date I would do something like this:
dt_variable := TO_DATE(<INPUT_DATE>,<DATE_FORMAT>)
and if it returned an error I would know the date is invalid. I have a different requirement now though. The day, month, and year are all separate text fields. I need to make sure the day is valid from 1-31, month is JAN-DEC (3 letter texts) and year is in the format YYYY. I also need to check if the date is valid, for instance no June 31st. Since I have 3 fields ow I can't use the to_date function is there any other function i can use to accomplish this. I have searched, but could not find anything and am hoping a sql expert might now of a method.
Don't try to validate the separate elements at all; concatenate them together with the appropriate separator, and treat the combined value as one; e.g.:
to_date(l_day || '/' || l_month || '/' || l_year, 'DD/MON/YYYY')
Using month names or abbreviations can be a problem as you're relying on the NLS settings matching. If you know the values will always be English you can specify the NLS_DATE_LANGUAGE with the optional third argument to to_date. It's often safer to use month numbers; and an unambiguous date format like YYYY-MM-DD. It doesn't sound like you have control over that though.

character_length Teradata SQL Assistant

I have to run column checks for data consistency and the only thing that is throwing off my code is checking for character lengths for dates between certain parameters.
SEL
sum(case when ( A.date is null or (character_length(A.date) >8)) then 1 else 0 end ) as Date
from
table A
;
The date format of the column is YYYY-MM-DD, and the type is DA. When I run the script in SQL Assistant, I get an error 3580 "Illegal use of CHARACTERS, MCHARACTERS, or OCTET_LENGTH functions."
Preliminary research suggests that SQL Assistant has issues with the character_length function, but I don't know how to adjust the code to make it run.
with chareter length are you trying to get the memory used? Becuase if so that is constant for a date field. If you are trying to get the length of the string representation i think LENGTH(A.date) will suffice. Unfortanatly since teradata will pad zeros on conversions to string, I think this might always return 10.
UPDATE :
Okay so if you want a date in a special 'form' when you output it you need to select it properly. In teradata as with most DBs Date are not store in strings, but rather as ints, counting days from a given 'epoch' date for the database (for example the epoch might be 01/01/0000). Each date type in teradata has a format parameter, which places in the record header instructions on how to format the output on select. By default a date format is set to this DATE FROMAT 'MM/DD/YYYY' I believe. You can change that by casting.
Try SELECT cast(cast(A.date as DATE FORMAT 'MM-DD-YYYY') as CHAR(10)) FROM A. and see what happens. There should be no need to validate the form of the dates past a small sample to see if the format is correct. The second cast forces the database to perform the conversion and use the format header specified. Other wise what you might see is the database will pass the date in a date form to SQL Assitant and sql assitant will perform the conversion on the application level, using the format specified in its own setting rather then the one set in the database.