Create Sybase function with exception handling - sql

I want to create function to use it in creation of view.
In my table there are strings (strings are consists only of 8 digits) that I'm converting into DATE.
My function is:
CREATE FUNCTION MY.FUNCTION(#date int)
RETURNS DATE
AS
BEGIN
RETURN CONVERT(DATETIME, #date)
END
If I use smth like SELECT FUNCTION('20170323') FROM TABLE it works as expected.
But if I'll try smth like SELECT FUNCTION('77777777') FROM TABLE it fails of course... But if it fail I need to retut NULL!
After some digging I have no result about function modification.
How to add exception handling in my function properly to return date on NULL if it fails?

use TRY_CONVERT instead of CONVERT, which would be :
CREATE FUNCTION TEST(#date varchar(50))
RETURNS DATETIME
AS
BEGIN
RETURN TRY_CONVERT(DATETIME, #date)
END
Result:
select [dbo].[TEST]('20171201') --output:2017-12-01 00:00:00.000
select [dbo].[TEST]('9999999999') --output: NULL

After a long investigation and lot of efforts I've found my solution:
CREATE FUNCTION MY_FUNCTION(#date CHAR(20))
RETURNS DATE
AS
BEGIN
RETURN
(CASE
WHEN ISDATE(#date) = 0
THEN NULL
ELSE CAST(#date AS DATE)
END)
END
Sybase method ISDATE() doing all magic in this case without throwing exception...

Related

SQL Scalar Function with Date Convert?

I am trying to make a scalar function, that will allow me to deal with null dates, which show as 1/1/1900. I can write out the logic in a query, but when I try to make it a function, whether I return a varchar or a date, it makes the format wrong.
This is the CASE for a column line in a select:
CASE WHEN CONVERT(VARCHAR(10), CAST(f.[DateStart] AS DATE),101) = '01/01/1900'
THEN ''
ELSE CONVERT(VARCHAR(10), CAST(f.[DateStart] AS DATE),101)
END AS 'DateStart'
If I make that a function, like this:
ALTER FUNCTION dbo.ConvertDate(#d datetime)
RETURNS VARCHAR
AS
BEGIN
RETURN (SELECT CASE WHEN CONVERT(VARCHAR(10), CAST(#d AS DATE),101) = '01/01/1900'
THEN ''
ELSE CONVERT(VARCHAR(10), CAST(#d AS DATE),101)
END);
END
GO
I returns a 0 for a legitimate date. If I return as a date, it returns a good date as 2019-10-08 and returns a null date as 1900-01-01. Anyone know how to get a function to return the way the case statement runs in the select, the way I want it to? Just trying to minimize repeating the same long logic throughout all my stored procs.
Thanks!

How to validate if a DateTime includes time in SQL Server

I have a stored procedure that takes a datetime parameter. I need to determine if the given datetime parameter includes the time.
Now, DATEPART(hour, #datetimeValue) = 0 doesn't work in my case because the datetime can be provided as 3/14/2019 0:00 which refers to as 12 AM and valid.
Return true if input is :
'3/14/2019 0:00'
'3/14/2019 15:00'
Return false only if input has no time :
'3/14/2019'
Thanks everyone for their input. It looks there is no solution to this other than changing the stored procedure parameter to varchar.
The requirement is not technically possible to handle in the Stored Procedure.
The DateTime parameter in your stored procedure will always contain a time aspect which is either explicitly passed to it or defaults to midnight of the passed in date. There is no way to know if the caller explicitly passed in a time aspect or not.
You have 2 options:
Change the incoming data type of the parameter to varchar and have the stored procedure parse that into a DateTime and handle validation.
Make the caller handle any validation having to do with time and drop this requirement from your code.
By default, a DATETIME includes a time, set to midnight (e.g. '00:00:00'), even if you aren't using the time portion. If your datetime's are strings, then here is a way to see if a time is part of the string (though it's a little hacky):
SELECT CASE WHEN CHARINDEX(':', '3/14/2019 0:00') > 0 THEN 1 ELSE 0 END -- Returns 1
SELECT CASE WHEN CHARINDEX(':', '3/14/2019 15:00') > 0 THEN 1 ELSE 0 END -- Returns 1
SELECT CASE WHEN CHARINDEX(':', '3/14/2019') > 0 THEN 1 ELSE 0 END -- Returns 0
Basically it's just searching the string and looking to see if a colon is in the string. It meets your criteria though.
The only solution I can think of is to ALTER your SP and use two (2) parameters instead, one is DATE and the second is TIME datatype as:
CREATE PROCEDURE HasTime(
#MyDate DATE = NULL,
#MyTime TIME = NULL
)
AS
BEGIN
IF #MyTime IS NULL
SELECT 'There is no time'
ELSE
SELECT 'There is time';
END;
EXEC dbo.HasTime '2019-01-01', NULL; --Or EXEC dbo.HasTime '2019-01-01';
EXEC dbo.HasTime '2019-01-01', '00:00:00';
Live Demo
This is crude, but it does what you need. If its throw away, eg part of some import it might be good enough for your needs.
I am sure this approach could be improved with more understanding on how to retreive details of the current executing SQL from within the PROC, I had a quick look at using query plans etc.
You could just fail back to this check if the time is midnight this makes it more efficient
ALTER PROCEDURE Hastime(#d AS DATETIME)
AS
BEGIN
-- Is there a easier way to get from DBCC INPUTBUFFER to a SQL variable?
CREATE TABLE #temp
(
spid INT,
eventtype NVARCHAR(30),
parameters INT,
eventinfo NVARCHAR(4000)
)
INSERT INTO #temp
(eventtype,
parameters,
eventinfo)
EXEC ('DBCC INPUTBUFFER(' + ##spid +') WITH NO_INFOMSGS')
-- Yes, we could do this better
IF EXISTS (SELECT *
FROM #temp
WHERE eventinfo LIKE '%:%')
SELECT 'Time'
ELSE
SELECT 'No Time'
END
go
EXEC dbo.Hastime
'2000/06/01'
go
EXEC dbo.Hastime
'2000/06/01 00:00:00'
You can do a check like this, but if the time is actually midnight, it will return not valid time:
-- returns 'NOTime'
SELECT CASE WHEN CAST('1/1/2019' AS TIME) = '00:00:00.000' THEN 'NOTime' ELSE 'TIME' END
-- returns 'TIME'
SELECT CASE WHEN CAST(GetDate() AS TIME) = '00:00:00.000' THEN 'NOTime' ELSE 'TIME' END
I would cast as date:
select (case when convert(date, #datetimeValue) = #datetimeValue then 'NoTime'
else 'HasTime'
end)
The only way is to pass a varchar type to teh procedure instead of datetime
otherwise we can not make difference between a date without time and a date at midnight
here you can check this restriction
The follwoing script illustrates that restriction:
declare #d as datetime
declare #d1 as datetime
set #d='01/01/2019'
set #d1='01/01/2019 00:00:00:00'
if #d=#d1 print 'equal' else print 'not equal'

Could somebody validate this function, please

Sorry to be a bother guys. I have been tasked with some code review at a client location and the laptop issued to me does not have SQL installed. While I am waiting for the installation to happen, wanted to get busy looking at the code and came across this gem
CREATE FUNCTION [dbo].[Uf_GetTotalDaysInMonth]
(
-- Add the parameters for the function here
#anydateofMonth datetime
)
RETURNS int
AS
BEGIN
-- Declare the return variable here
DECLARE #totalDaysInMonth int
-- Add the T-SQL statements to compute the return value here
DECLARE #givendate datetime
SET #givendate = STR(Year(#givendate)) + '-' + STR(Month(#givendate) + 1) + '-01'
select #totalDaysInMonth = datepart(dd, dateadd(day, -1, #givendate))
-- Return the result of the function
RETURN #totalDaysInMonth
END
Ignoring the use of needless extra variable, I believe that this function will crash in December
STR(Month(#givendate) + 1)
will evaluate to 13 and will give an out of scope date error. Could someone please validate this for me?
You vill get error in your function when pass #anydateofMonth December date.
You can use this:
CREATE FUNCTION [dbo].[Uf_GetTotalDaysInMonth]
(
-- Add the parameters for the function here
#anydateofMonth datetime
)
RETURNS int
AS
BEGIN
DECLARE #nextMonth datetime
SET #nextMonth = dateadd(m, 1, #anydateofMonth)
RETURN (SELECT Day(dateadd(d, -Day(#nextMonth), #nextMonth)))
END
http://sqlfiddle.com/#!6/185c9/7
no error on SQL Fiddle, just 13 as output

IsDate Function in SQL evaluates invalid dates as valid

I am running a SQL Statement against imported data from Excel Files.
In this SQL I am checking if the users have entered dates properly by using IsDate function. Since this is a raw data that hasn't been converted yet, all dates are stored in a varchar data type field.
In some circumstances IsDate returns 1 (valid date) when there is clearly an incorrect date format entered by the user.
For Example:
07/001/2012
2012-07-002
007/002/2012
Any Suggestions on how to handle this problem?
SELECT *
FROM tblImport
WHERE (ISDATE(dt) = 0
AND (dt is not null AND dt <> ''))
Thanks!
p.s. Smacking users' did not help.
I do a lot of data conversion work and here is a function that I created and use it practically everyday to weed out the bad dates:
CREATE FUNCTION dbo.fnCheckDate
(#InDate nvarchar(50))
RETURNS DATETIME
AS
BEGIN
declare #Return DATETIME
select #return = CASE WHEN ISDATE(#InDate) = 1
THEN CASE WHEN CAST(#InDate as DATETIME) BETWEEN '1/1/1901 12:00:00 AM' AND '6/6/2079 12:00:00 AM'
THEN #InDate
ELSE null
END
ELSE null
END
return #return
END
GO
Results:
SELECT dbo.fnCheckDate('07/001/2012') --> Returns 2012-07-01 00:00:00.000
SELECT dbo.fnCheckDate('2012-07-002') --> Returns 2012-07-01 00:00:00.000
SELECT dbo.fnCheckDate('007/002/2012') --> Returns 2012-07-01 00:00:00.000
SELECT dbo.fnCheckDate('00/002/2012') --> Returns Null
SELECT dbo.fnCheckDate('006/031/2012') --> Returns Null
SELECT dbo.fnCheckDate('') --> Returns Null
Try setting the dateformat first - that worked for me when I was seeing exceptions.
set dateformat dmy
select IsDate(<column>)
from Table
maybe just check dt's LEN? however, it can not handle cases when the len is valid. maybe input validation should happen in the frontend?

SQL 2008 CASE statement aggravation

Why does this fail:
DECLARE #DATE VARCHAR(50) = 'dasf'
SELECT CASE WHEN ISDATE(#DATE) = 1 THEN CONVERT(date,#DATE) ELSE #DATE END
Msg 241, Level 16, State 1, Line 2
Conversion failed when converting date
and/or time from character string.
Why is it trying to convert dasf to date when it clearly causes ISDATE(#DATE) = 1 to evaluate to false...
If I do:
SELECT ISDATE(#DATE)
The return value is 0.
CASE returns a single type. In this case, the type is Date, found from your THEN clause. It is implicitly converting the ELSE clause result to Date to match.
You must choose a single type to be returned by CASE. It cannot be used to return sometimes Date and sometimes varchar.
from MSDN:
http://msdn.microsoft.com/en-us/library/ms181765.aspx
Return Types
Returns the highest
precedence type from the set of types
in result_expressions and the optional
else_result_expression. For more
information, see Data Type Precedence
(Transact-SQL).
and then following that link: http://msdn.microsoft.com/en-us/library/ms190309.aspx
8) date
27) varchar
It's not clear what you want, so it's hard to offer alternatives (I don't know if the CASE is part of a larger query or script), but here's a couple things you can do:
-- choose a single return type per CASE expression
SELECT
CASE
WHEN IsDate(#Date) = 1
THEN convert(date, #Date)
ELSE null
END as [Date],
CASE
WHEN IsDate(#Date) = 1
THEN null
ELSE #Date
END as [VarChar]
--use control flow to select what you want.
IF IsDate(#Date) = 1
THEN
SELECT convert(date, #Date)
ELSE
SELECT #Date
try this:
DECLARE #DATE VARCHAR(50) = 'dasf'
SELECT CASE
WHEN ISDATE(#DATE)=1 THEN CONVERT(char(23),CONVERT(date,#DATE),121)
ELSE #DATE
END
It will basically format your valid date and leave the non-dates alone. Is that what you are after?
actual working sample:
DECLARE #YourTable table (DATE VARCHAR(50))
INSERT #YourTable VALUES ('dasf')
INSERT #YourTable VALUES ('1/1/2010')
SELECT
CASE
WHEN ISDATE(DATE)=1 THEN CONVERT(char(23),CONVERT(datetime,DATE),121)
ELSE DATE
END AS DATE
FROM #YourTable
OUTPUT:
DATE
--------------------------------------------------
dasf
2010-01-01 00:00:00.000
(2 row(s) affected)
In the working example, I made a substitute from date data type to datetime because I'm on SQL Server 2005 and date datatype is SQL Server 2008 only.