SQL Scalar Function with Date Convert? - sql

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!

Related

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'

Trying to eliminate '1900-01-01' from converted date column

I realise that there are other threads covering this but I can't find one that solves my problem.
I have three text columns that either contain a date or nothing (null), I'm converting the text to a date in order to use date filters once the date makes it to Excel.
Obviously, the null values get converted to '1900-01-01' but I can't find a way to show them as blanks, even with a subquery.
This is what I'm doing...
Select top 999999999
OtherFields,
case when Engdate = '19000101' then '' else Engdate end as 'EngDate',
OtherOtherFields
from
(
select
OtherFields,
case
when VAR_AppID = '3' then nullif( CONVERT(date,concat(right([2201],4),
SUBSTRING([2201],4,2), left([2201],2))),null)
else nullif( CONVERT(date,concat(right([3429],4),
SUBSTRING([3429],4,2), left([3429],2))),null) end as 'Engdate',
case when ([2201] is not null and Len([2201]) = 10 ) then
CONVERT(date,concat(right([2201],4), SUBSTRING([2201],4,2), left([2201],2)))
else null end as 'EngagementRec',
OtherOtherFields
from
Tables
)
The fields [2201], and [3429] are the text fields that contain the dates in 'dd/mm/yyyy' format.
Am I supposed to covert back to a text format to allow the '' to show a blank - and if so, will the dates still be recognised as dates?
As per answer of Anthony Green on sqlservercentral, you may try:
ISNULL(CONVERT(varchar, DateTimeColumnName,23),'') as DateTimeColumnName
Yes, Converting them text first won't do any bad (I think).
Try the following:
Select top 999999999
OtherFields,
case when Engdate = '19000101' then '' else CONVERT(CHAR(10), EngDate, 120) end as 'EngDate' end as 'EngDate',
OtherOtherFields
from
(
select
OtherFields,
case
when VAR_AppID = '3' then nullif( CONVERT(date,concat(right([2201],4),
SUBSTRING([2201],4,2), left([2201],2))),null)
else nullif( CONVERT(date,concat(right([3429],4),
SUBSTRING([3429],4,2), left([3429],2))),null) end as 'Engdate',
case when ([2201] is not null and Len([2201]) = 10 ) then
CONVERT(date,concat(right([2201],4), SUBSTRING([2201],4,2), left([2201],2)))
else null end as 'EngagementRec',
OtherOtherFields
from
Tables
)
Thanks.
As you may know, converting an empty string to the date datatype returns 1900-01-01.
Example SQL:
SELECT CAST ('' as date) AS [thedate]
Returns:
thedate
1900-01-01
The NULL value, when casted to the date datatype is still NULL.
Based on your code, I think all you have to do is change this line:
case when Engdate = '19000101' then '' else Engdate end as 'EngDate',
to:
case when Engdate = '19000101' then NULL else Engdate end as 'EngDate',
I'm closing this one myself as I can't find anything that works.
Any combination of case statments, ISNULL(), NULLIF() etc result in either NULL or '1900-01-01'. Converting to text makes the date field unusable in Excel.
As the data will be coming into an Excel pivot table, I don't desperately need to resolve this as pivot tables eliminate null values anyway.
...I might come back to this if I need to show the data in a list.
Thanks to everyone that made suggestions!
According to the ms help on the Concat function (which is sql 2012 onwards not 2008)
https://learn.microsoft.com/en-us/sql/t-sql/functions/concat-transact-sql?view=sql-server-2017
If CONCAT receives arguments with all NULL values, it will return an empty
string of type varchar(1).
A zero length string will be implicitly converted to zero giving you the zero date in sql server. You need to handle the nulls before trying to convert the way you have done it.
Maybe use Try_convert instead.
Use try_convert with the right option.
TRY_CONVERT(Datetime, columnwithstringdate, 103)

Create Sybase function with exception handling

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...

SQL-Should I use nested Query to first correct erroneous data and then convert to datetime?

There is one column: OpenedDate with datatype: varchar(8) and I want to convert it into datetime but since OpenedDate has erroneous values of 0's.
First- I want to convert the 0's into NULLs using the query:
CASE WHEN Opened_dt = '0'
then 'NULL'
else Opened_dt
end as 'Opened_dt_converted'
Now, I want to use the above results to convert the datatype to DateTime using the syntax:
CONVERT(DATETIME, 'Opened_dt_converted',120)
I was thinking if I should use Nested query or create a stored procedure but I am not sure how can I use the nested query for this type of situation?
Basically, I want this whole query in one stored procedure. Could you please help me in achieving that task?
Thanks in advance!
Geetanjali
If you are using SQL Server 2012+, just use try_convert():
select try_convert(DateTime, OpenedDate, 120)
If it fails, then you'll get NULL. In older versions, you would just use a case:
select (case when OpenedDate like '[0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9]'
then convert(DateTime, OpenedDate, 120)
end)
Note: I just put in a format that would often work for date. The time component is similar.
Sub-select or CTE should work for you.
SELECT CONVERT(DATETIME, Opened_dt_converted, 120) AS dt_converted
FROM (SELECT CASE
WHEN Opened_dt = '0' THEN NULL
ELSE Opened_dt
END AS Opened_dt_converted
FROM yourtable) a
or use Case statement to skip the zero from convertion
SELECT CASE
WHEN Opened_dt = '0' THEN NULL
ELSE CONVERT(DATETIME, Opened_dt, 120)
END AS dt_converted
FROM yourtable

SQL Server: Using a case statement to see if a date is NULL and if it is returning ' '

I have a column in my select statement that looks like this:
SELECT CASE WHEN fu.SentOutDate IS NULL THEN '' ELSE fu.SentOutDate END
This returns 1900-01-01 00:00:00.000 for the ones that would otherwise be NULL
I know this because when I put in just fu.SentOutDate it comes up as NULL
Why does this happen and how can I just get it to return a blank value?
Try converting the date to a string so it doesn't try to convert '' to a date:
(CASE WHEN fu.SentOutDate IS NULL THEN '' ELSE CONVERT(varchar,fu.SentOutDate) END)
It's casting your '' to a DATETIME, since your other column you'd return is a datetime column.
SELECT CASE WHEN 1=1 THEN '' ELSE GETDATE() END
will give you the same value...
You can convert this to a varchar(32), but I'm not certain of the ramifications
SELECT CASE WHEN 1=1 THEN '' ELSE CAST(GETDATE() AS varchar(32)) END
A column can only return one data type - DATETIME != string/VARCHAR.
If you want a zero length string in the event of the value being NULL, you have to explicitly change the data type, using CAST/CONVERT to change the non-NULL value to a VARCHAR/etc data type.
If you're just checking for NULL values, you might try ISNULL() and cast the date as a varchar.
SELECT ISNULL(CAST(fu.SentOutDate AS VARCHAR(50)), '') AS SendOutDate
FROM tablename
It sounds like you're displaying this value in a GUI or client somewhere. In my opinion, the best practice is to convert it from the NULL value there, not in the query.
If you ever create a database that scales to millions of users, you want a little processing as possible in the database and as much as possible in the client. Doing conversion of a date to character is an unneeded load on the system (character calculation is always much slower than math).
This may work:
case when ISNULL(convert(varchar, a.rec_dt, 108), '00:00:00')='00:00:00' then ''
else CAST(rec_dt as varchar)