Validate a date comprised of 3 fields in PLSQL - sql

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.

Related

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

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.

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 =?;

Oracle PL/SQL : to_date() format not considered

When I execute this in PL/SQL Developer :
SELECT to_date('29/03/17 14:05','DD/MM/RR HH24:MI') FROM dual;
Here's what I get :
3/29/2017 2:05:00 PM
How is this possible ? I use HH24 but it seems like it's HH that's being used instead. The day and month are also not in the format I entered.
What you are doing with the to_date method is parsing the string into a date object. If you then want to output the date object as string with a different format you should use the to_char method.
Example:
SELECT to_char(
to_date('29/03/17 14:05','DD/MM/RR HH24:MI'),
'DD/MM/RR HH24:MI'
) FROM dual;
Ok, conceptual excercise coming up
Which of these dates represents the 1st January 2017?
01/01/2017
2017-01-01
01-JAN-2017
That's right, all of them. The date datatype is not a format, it stores the value of the date, not how it appears.
If using Oracle, adjust your NLS_DATE_FORMAT to match your expectation, but again, this is just how the system will display the date, not how it stores it.
(N.B. This answer is more to give more clarity to the other answers but it's too long for a comment.)
Oracle stores DATEs (and TIMESTAMPs etc) in its own specific format. Us humans represent dates in a variety of different formats and we deal with strings. Even us humans can get confused over what a date string represents, given no context - e.g. 03/09/2017 - is that the 3rd of September, 2017 or the 9th of March 2017?
So, when you pass a date into Oracle, you need to convert it into Oracle's date format by passing a string in and telling Oracle what the date format of that string is. This can be done using to_date() or via the DATE literal (which is always in yyyy-mm-dd format).
Conversely, when you want to read something that's stored in Oracle's DATE datatype, you need to tell Oracle how you want it to be displayed, which you can do by using to_char() with the appropriate format mask.
If you fail to explicitly convert the string-to-a-date or date-to-a-string, then Oracle uses the format specified in the NLS_DATE_FORMAT to decide how to do the conversion.
In your case, you didn't specify how you wanted your date to be displayed, so Oracle has to use to_char() along with the format contained in your NLS_DATE_FORMAT in order to display the date as a string, and clearly that's different to the format you passed the date-string in as.

Jasper - Oracle - How to filter by date

I am trying to filter a query by date.
I have this line in my query:
AND (the_date like CONCAT (TO_DATE($P{THE_DATE}, 'YYYY-MM-DD'),'%'))
However, I just cannot seem to compare the parameter date to the database date effectively.
Database date is of type DATE. Parameter is of type String.
I've also tried:
AND (TO_CHAR(the_date) like CONCAT ($P{THE_DATE,'%')
Are my data types wrong? I've tried others but to no avail. Is my query wrong?
I'm using iReport... I looked for some kind of debugging option to see what is actually being executed but didn't find any.
When you want to compare DATEs, you need to convert the literal into DATE using TO_DATE.
No need to use LIKE operator. You could either useTRUNC on the DATE column, however, that would suppress any regular index usage. It would be better to use a DATE RANGE condition.
Remember, DATE has both date and time elements.
For example,
WHERE
the_date >= TO_DATE('14-MAY-2015','DD-MON-YYYY')
AND
the_date < TO_DATE('14-MAY-2015','DD-MON-YYYY') +1;
Instead of literals in above example, you could use your INPUT parameter or the local variable which you have defined as string.
WHERE
the_date >= TO_DATE(in_date,'DD-MON-YYYY')
AND
the_date < TO_DATE(in_date,'DD-MON-YYYY') +1;
The format stored in the databases was like 12/MAY/15.
Although I am convinced I attempted this in the input parameter which was of type String, this proved to be the magic answer :) my input was '12/MAY/15' and it worked.

Oracle to_date() incorrect output

There must be a very simple answer, but I can't find it anywhere.
I have the following which is a section of my select statement:
case when q.renewal_date is not null then
to_date(q._renewal_date, 'DD/MM/YYYY')
else
to_date(w.END_DATE, 'DD/MM/YYYY')
end END_DATE,
according to all of the docs I can find the MM should give the month in numbers however I'm getting results such as:
30-SEP-12
26-JUN-11
30-SEP-12
It's also interesting that they're hyphenated (-) and not with slashes (/).
So what's the reason for this and how do I achieve what I want?
Assuming w.end_Date and q._renewal_date are actual dates, you want to to_char them, not to_date. At present I would say you are seeing the dates in the format specified by your NLS settings. (If they are not dates, you are converting them to dates, but still letting your NLS settings choose the format you view it in)
As you are TO_DATEing the value it is stored by Oracle internally as a date. It is displayed back to you using your NLS_DATE settings value which i would assume are set to DD-MON-YY by default.
You can check with
SELECT *
FROM v$parameter
WHERE name = 'nls_date_format';
You'll need to either alter your NLS_DATE_FORMAT setting (either for your session or for the DB) or TO_CHAR the output to the format you want to see.
to_date converts a string to a date. The code you have is taking a string (q._renewal_date) in 'DD/MM/YYYY' format and converting it to a date. What you are seeing is the default rendering of the date field.
Depending on what type q._renewal_date is, you probably need to use a different conversion/formatting function.