I have a strange bug that I cannot resolve (with a one line function).
This code works:
DECLARE #TestDate datetime = '2013-05-01 23:15:11'
select IsNull(convert(varchar(max), #TestDate, 120), 'null') as 'test1'
Displays: 2013-05-01 23:15:11
CREATE FUNCTION [dbo].[DateOrNullToChar] (#InputDate date)
RETURNS VARCHAR(40)
BEGIN
return ISNULL(convert(varchar(40),#InputDate, 120),'null');
END
select dbo.DateOrNullToChar('2013-05-01 23:15:11') as 'result'
Returns: 2013-05-01 (no time)
I have also tried varchar(max).
The purpose of the function is for something like this:
Set #ErrorMessage = ' #ArrivalDate=' + dbo.DateOrNullToChar(#ArrivalDate) +
' #DepartureDate=' + dbo.DateOrNullToChar(#DepartureDate);
If any one value is null, the whole value becomes null. So I want to see the string 'null' when a date has a null value.
#InputDate should be datetime or datetime2 if you want time to be shown
The clues are in the code...
#TestDate datetime
#InputDate date
You need to make the parameter type to be datetime instead of date:
CREATE FUNCTION [dbo].[DateOrNullToChar] (#InputDate datetime)
It's silently converting the string to your date parameter type and thus dropping the time portion.
Related
I have two function.
fn_validate_date
fn_validation
fn_validate_date code:
CREATE DEFINER=`root`#`localhost` FUNCTION `fn_validate_date`(
`dt_date` DATE
)
RETURNS date
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT 'Returns the associated value of given attribute for given employee for a particular date.'
BEGIN
SET dt_date = IF(dt_date IS NULL OR dt_date ='', CURRENT_DATE, dt_date);
RETURN dt_date;
END
fn_validation code:
CREATE DEFINER=`root`#`localhost` FUNCTION `fn_validation`(
`dt_date` DATE
)
RETURNS date
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
RETURN fn_validate_date(dt_date);
END
Now when I am calling fn_validate_date as below
SELECT `fn_validate_date`(null);
It's working well but when I calling fn_validation it's giving me an error.
SELECT `fn_validation`(null);
My question is why I didn't get error while calling fn_validate_date?
In fn_validate_date, the dt_date-parameter is type of date and you are comparing it to a string datatype. No need for that. Date datatype cannot contain ''. It either is NULL or has a date value in it.
So instead of:
SET dt_date = IF(dt_date IS NULL OR dt_date ='', CURRENT_DATE, dt_date);
You can simply use:
return ifnull( dt_date, current_date() );
I disable strict mode and it's working well.
SET sql_mode =''
To disable strict mode in MySQL. I am not sure why MySql short-circuited IF condition as I am passing NULL in the input parameter of fn_validation.
I have CSV file, contains date field that has either:
1. %Y-%m-%d
2. %m/%d/%Y
3. empty string
The code I use for importing:
LOAD DATA INFILE ...
SET EpStartDate = IFNULL (DATE(#v_EpStartDate), STR_TO_DATE(#v_EpStartDate, '%m/%d/%Y')),
...
But this code throws warning about every %m/%d/%Y date, and about each empty cell.
It's very important to me to show only crucial warnings, when the data is wrong, like if the date is 03/18/20095.
Any idea how to do that?
It's doable using custom function:
DROP FUNCTION IF EXISTS PARSE_DATE;
DELIMITER //
CREATE FUNCTION PARSE_DATE ( str VARCHAR(255) ) RETURNS DATE
BEGIN
IF str in ('',' ','.') THEN RETURN null;
ELSEIF str like '__/__/____' THEN RETURN STR_TO_DATE(str, '%m/%d/%Y');
ELSEIF str like '__-__-____' THEN RETURN STR_TO_DATE(str, '%m-%d-%Y');
ELSE RETURN DATE(str); /*may throw a warning*/
END IF;
END//
DELIMITER ;
Read the IFNULL function details here.enter link description here. The code
IFNULL(expr1, expr2)
returns expr1 if expr1 is NOT null, and expr2 if expr1 IS null.
So when the date variable is not empty, it is returned as it is (without checking for format, as that is done in expr2 in your code above) and when it is empty, it is checked against the date format.
Thus your program gives you warnings because you are checking for date formats on empty strings and at the same time input the non-empty strings without checking for their date format.
I have one DATE input parameter for a procedure ex: IN p_date DATE.I want to validate this input DATE parameter format inside a procedure which should be in YYYY-MM-DD format. If the input parameter is having characters or date format is wrong it should through an exception using SIGNAL.
Please find the below code what i written
CREATE PROCEDURE `validation_check`(IN pdate_time DATE)
BEGIN
DECLARE InputValidation CONDITION FOR SQLSTATE '45000';
DECLARE dateValidation CONDITION FOR SQLSTATE '45000';
/* Doing NULL validation */
IF pdate_time IS NULL THEN
SIGNAL InputValidation
SET MESSAGE_TEXT='pdate_time should not be empty.';
END IF;
/* Doing Date format validation
IF STR_TO_DATE(pdate_time,'%Y-%m-%d') != pdate_time THEN
SIGNAL dateValidation
SET MESSAGE_TEXT='Input Date format should be in YYYY-MM-DD.';
END IF;
*/
/* Doing Date format validation */
IF pdate_time NOT REGEXP '/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/' THEN
SIGNAL dateValidation
SET MESSAGE_TEXT='Input Date format should be in YYYY-MM-DD.';
END IF;
SELECT pdate_time;
END
Thanks,
Sagar
TL;DR
Regular expression will fail in this case, because they may only check date format, but not if it is a valid date (as, for example, '2014-02-30' has correct format but invalid data for date)
Use string functions
Concept
The solution is - yes, use string functions. However, regular expressions will also be useful - to check if format was good, you still need to check date itself. Since validation of date is a single separated issue - you should create a function for it. That function will accept string and return result as boolean value - so either date is ok or not. This will be re-usable and, therefore, more flexible.
Code
Here we go with the function:
CREATE FUNCTION VALIDATE_DATE(d VARCHAR(255))
RETURNS INT
BEGIN
DECLARE date_year VARCHAR(255) DEFAULT '';
DECLARE date_month VARCHAR(255) DEFAULT '';
DECLARE date_day VARCHAR(255) DEFAULT '';
DECLARE ym_delim INT DEFAULT 0;
DECLARE md_delim INT DEFAULT 0;
-- First, if it's just not xxx-yyy-zzz format:
SET ym_delim = LOCATE('-', d);
SET md_delim = LOCATE('-', d, ym_delim+1);
IF !ym_delim || !md_delim THEN
RETURN FALSE;
END IF;
-- Second, if resulted members are not YYYY, MM or DD:
SET date_year = SUBSTR(d, 1, ym_delim-1);
SET date_month = SUBSTR(d, ym_delim+1, md_delim-ym_delim-1);
SET date_day = SUBSTR(d, md_delim+1);
IF date_year NOT REGEXP '^[0-9]{4}$'
|| date_month NOT REGEXP '^[0-9]{2}$'
|| date_day NOT REGEXP '^[0-9]{2}$' THEN
RETURN FALSE;
END IF;
-- Finally, check if date itself is ok, like 2014-02-30 isn't ok:
IF DATE(CONCAT(date_year, '-', date_month, '-', date_day)) IS NULL THEN
RETURN FALSE;
END IF;
RETURN TRUE;
END//
DELIMITER ;
As you can see, we have three cases, when date validating fails:
First, if there are no proper delimiters (which are -). Then year, month and day just can't be found
Second, if extracted year, month and day part are just bad (for instance, date was foo-bar-baz). That's why we can't use date functions to extract those parts and so we have to use string functions.
Third - finally, if our date parts seems to be good, there still may be false result because of invalid combination (2014-13-01 has wrong month, for example).
Seems to be a solution
There is, however, STR_TO_DATE() function which may look like solution. Unfortunately, it will pass date parts which are not in corresponding format (such as 2014-1-1) - thus, it can't be used for direct format validation. That is why I used separate stored function instead.
What will be passed
All YYYY-MM-DD dates, which are correct in terms of MySQL, will be passed. That is, early dates, such as '0001-01-01' are correct :
mysql> SELECT VALIDATE_DATE('0001-01-01');
+-----------------------------+
| VALIDATE_DATE('0001-01-01') |
+-----------------------------+
| 1 |
+-----------------------------+
1 row in set (0.00 sec)
And, in fact, they should be correct, because they are valid for MySQL. However, such things as 001-01-01 won't be passed even despite fact, that such strings are correct for MySQL dates too:
mysql> SELECT VALIDATE_DATE('001-01-01'), DATE('001-01-01');
+----------------------------+-------------------+
| VALIDATE_DATE('001-01-01') | DATE('001-01-01') |
+----------------------------+-------------------+
| 0 | 0001-01-01 |
+----------------------------+-------------------+
1 row in set (0.00 sec)
And that is derived from your format expectations - you should filter all the things, which do not have YYYY-MM-DD format exactly, thus, you'll have such results.
Duration = isnull(FunctionA(DateA,DateB),'')
The Function above calculates number of days and if day is null it displays
the value 0 instead of blank value
How can I change the above code to so that it shows blank and not 0 for value null?
If your function returns an integer the result from isnull will also be an integer. In the case the return value is null you will have an implicit conversion to integer for '' and that will be 0.
Try this:
declare #xx int
select isnull(#xx,'')
Result:
-----------
0
You can have the space if you first cast the return value from your function to varchar.
declare #xx int
select isnull(cast(#xx as varchar(10)),'')
Result:
----------
.
If your function returns 0 instead of null you can use nullif to get a null value before you cast to varchar.
declare #xx int = 0
select isnull(cast(nullif(#xx, 0) as varchar(10)),'')
Summary:
You need this:
Duration = isnull(cast(FunctionA(DateA,DateB) as varchar(10)),'')
or this
Duration = isnull(cast(nullif(FunctionA(DateA,DateB), 0) as varchar(10)),'')
If Duration is datatype int then you can't change that to an empty string (blank). You'll either have to change that to a string datatype (varchar for instance) or be okay with 0. int can either be NULL (if it is allowed) or a valid integer value. A blank string is not a valid integer value.
I use case statements and casting to do this.
Example:
case when columnX <> 0 then cast(columnX as nvarchar) else '' end
Basically, you're changing your numeric to show either as a character or a blank. You will have to do all your math before you change to nvarchar though, because outside of this, it becomes a string. It would be helpful if BLANK was a command and worked with numeric values though.
Hope this helps someone.
Is FunctionA returning 0 instead of null? The code you've written may be ok, but if FunctionA never returns null then...
You could declare Duration as a sql_variant datatype and allow implicit conversion to occur
so something like this should work
declare #DURATION sql_variant
select COALESCE(#DURATION, '')
set #DURATION=1
select COALESCE(#DURATION, '')
I'm writing some stored procedures in SQL Server 2008. Is the concept of optional input parameters possible here?
I suppose I could always pass in NULL for parameters I don't want to use, check the value in the stored procedure, and then take things from there, but I was interested if the concept is available here.
You can declare it like this:
CREATE PROCEDURE MyProcName
#Parameter1 INT = 1,
#Parameter2 VARCHAR (100) = 'StringValue',
#Parameter3 VARCHAR (100) = NULL
AS
/* Check for the NULL / default value (indicating nothing was passed) */
if (#Parameter3 IS NULL)
BEGIN
/* Whatever code you desire for a missing parameter */
INSERT INTO ........
END
/* And use it in the query as so */
SELECT *
FROM Table
WHERE Column = #Parameter
Yes, it is. Declare the parameter as so:
#Sort varchar(50) = NULL
Now you don't even have to pass the parameter in. It will default to NULL (or whatever you choose to default to).
In SQL Server 2014 and above at least, you can set a default, and it will take that and not error when you do not pass that parameter.
Partial example: the third parameter is added as optional. Execution (exec) of the actual procedure with only the first two parameters worked fine.
exec getlist 47,1,0
create procedure getlist
#convId int,
#SortOrder int,
#contestantsOnly bit = 0
as
The default mentioned in previous answers only works for simple cases. In more complicated cases, I use an IF clause near the beginning of the stored procedure to provide a value, if the parameter is NULL or empty and calculations are required.
I often use optional parameters in the WHERE clause, and discovered that SQL does not short circuit logic, so use a CASE statement to make sure not to try to evaluate NULL or empty dates or unique identifiers, like so:
CREATE Procedure ActivityReport
(
#FromDate varchar(50) = NULL,
#ToDate varchar(50) = NULL
)
AS
SET ARITHABORT ON
IF #ToDate IS NULL OR #ToDate = '' BEGIN
SET #ToDate = CONVERT(varchar, GETDATE(), 101)
END
SELECT ActivityDate, Details
FROM Activity
WHERE
1 = CASE
WHEN #FromDate IS NULL THEN 1
WHEN #FromDate = '' THEN 1
WHEN ActivityDate >= #FromDate AND ActivityDate < DATEADD(DD,1,#ToDate) THEN 1
ELSE 0
END