Could somebody validate this function, please - sql

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

Related

SQL Format Date User Defined Function Error

I have a user defined function that looks like this;
 
ALTER FUNCTION [dbo].[func_format_date]
(
    -- Add the parameters for the function here
    #input VARCHAR(6)
)
RETURNS VARCHAR(6)
AS
BEGIN
    -- Declare the return variable here
    DECLARE #mon CHAR(2),
     #day char(2),
        #year char(4),
        #output DATETIME
 
    -- Return the result of the function
    SELECT #mon = LEFT(#input, 2), #day =SUBSTRING(#input, 3,2), #year = RIGHT(#input,4)
 
SELECT #output = #year+#mon+#day
 
RETURN CONVERT(VARCHAR(6), #output, 12)
END
The goal is to be able to pass in a date say “022019” (mmddyy) and the function will format the date as such “190220” (yymmdd). This seems to work only sometime as if I pass in the following dates see the results below; it seems to be inconsistent in terms of the dates it accepts vs the dates that throw an error
022019 (Works fine)
032019 (Works fine)
021019 (Results in a “The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.” error)
030519 (Results in a “The conversion of a varchar data type to a
datetime data type resulted in an out-of-range value.” error)
I have checked to ensure the default language is correct and it is. Can someone help me to figure this out?
First, the reason for this error - your #input parameter is varchar(6), you read 1st and 2nd characters as month, 3rd and 4th as day, and 3rd, 4th, 5th and 6th as year. When #input is equal to '021019', your year will be 1019 and this is outside the datetime datatype range.
Second, change your logic:
make your #input parameter varchar(8)
generate datetime value using DATETIMEFROMPARTS() function if you have four digit year
If you want to fix your function, next may help:
ALTER FUNCTION [dbo].[func_format_date]
(
#input VARCHAR(6)
)
RETURNS VARCHAR(6)
AS
BEGIN
-- Declare the return variable here
DECLARE
#mon VARCHAR(2),
#day VARCHAR(2),
#year VARCHAR(2),
#output DATETIME
-- Return the result of the function
SELECT
#mon = SUBSTRING(#input, 1, 2),
#day = SUBSTRING(#input, 3, 2),
#year = SUBSTRING(#input, 5, 2)
-- Generate datetime value
SELECT #output = CONVERT(datetime, #year + #mon + #day, 12)
-- or if your dates are after 2000
--SELECT #output = DATETIMEFROMPARTS(2000 + #year, #mon, #day, 0, 0, 0, 0)
-- Return value
RETURN CONVERT(VARCHAR(6), #output, 12)
END
You Can Simply your function using below Stmt.
If no Date Validation Required you can interchange values.
select RIGHT(#input,2) + LEFT(#input, 2) + SUBSTRING(#input, 3,2)
If Date Validation Required use below.
select Convert(varchar, Convert(datetime, LEFT(#input, 2) + '/' + SUBSTRING(#input, 3,2) + '/' + RIGHT(#input,2), 1), 12)

Direct access slower than using functions?

I was conducting some performance testing and have discovered something quite strange. I have set up a short script to time how long it takes to perform certain actions.
declare #date date
declare #someint int
declare #start datetime
declare #ended datetime
set #date = GETDATE()
DECLARE #count INT
SET #count = 0
set #start = GETDATE()
WHILE (#count < 1000)
BEGIN
--Insert test script here
END
set #ended = GETDATE()
select DATEDIFF( MILLISECOND, #start, #ended)
The table I was running tests againsts contains 3 columns, MDay, and CalDate. Every calendar date has a corresponding M(Manufacturing)Day. The table may look something like this:
MDay | CalDate
1 | 1970-01-01
2 | 1970-01-02
I wanted to test how efficient one of our functions was. This function simply takes in a date and returns the int MDay value. I used direct access, basically the same thing without the function, and tests resulted in this method take twice as long! Code I inserted into the loop is provided below. I used a random date in an attempt to eliminate caching (if exist).
Function
select #someint = Reference.GetMDay(DATEADD( D, convert(int, RAND() * 1000) , #date))
Definition for above
create Function [Reference].[GetMDay]
(#pCaLDate smalldatetime
)
Returns int
as
Begin
Declare #Mday int
Select #Mday = Mday
from Reference.MDay
where Caldate = #pCaLDate
Direct
select #someint = MDay from Reference.MDay where CalDate = DATEADD( D, convert(int, RAND() * 1000) , #date)
I even tried using a static #date for my direct code and the difference in times are negligible, so I know the convert call isn't holding it back.
What the heck is going on here?
Take a look at http://msdn.microsoft.com/en-us/library/ms178071%28v=sql.105%29.aspx is the execution plan the same on your sql server for both methods?

Convert Varchar to DateTime - Varchar has no character to divide date parts

Is it possible to convert a varchar of 5122012 to a datetime of 05/12/2012?
Thanks
First of all you can't do it without trailing zeros of day and month. So your input data must be something like:
05122012
Second it is better to use the "native" format of:
20121205
what the server understands anyway
If it is the only way to get the data as you presented you would need a function to test and return the date
Basic function would be like this (SQL Server 2008 example):
This function works if the input data is: 05122012 if you need without trailing 0 you need to add the checks for that to the function
create function [dbo].[Str2Date] ( #data as varchar(8))
returns datetime
AS
begin
declare #day char(2), #mon char(2), #year char(4)
set #day = substring(#data,1,2)
set #mon = substring(#data,3,2)
set #year = substring(#data,5,4)
set #data = #year+#mon+#day
return convert (datetime,#data,112)
end
select dbo.Str2Date('05122012')

Put condition on select command : charindex - SQL

I need help with the following query. I want to put a condition on the returned value of select command which is an integer between 0 and any number.
DECLARE #month varchar(50);
set #month ='01-03-05-07-09-11'; <--- clarification: these are all months
Declare #cur_month int;
set #cur_month = Month(GetDate());
select charindex(str(#cur_month), #month);
I essentially want
If ( select charindex(str(#cur_month), #month))
// successful
I get error
An expression of non-boolean type specified in a context where a
condition is expected, near 'print'.
I tried CAST and CONVERT but to no Avail. As you can see, I am trying to see if the current month is in my #month field, I want to execute certain actions if it is.
Taking the answer from #PaulStock with a couple tweaks will work better.
Changes from PaulStock's answer
Left pad for leading zeros. 2 != 02 and wouldn't match.
Add hyphens at the beginning and end of the string so each value is in the form -XX- I included this in case you realize you won't be having the leading zeros. If you'll always have leading zeros you won't need this.
Actual code
DECLARE #month VARCHAR(50);
set #month ='03-05-12-09';
set #month = '-' + #month + '-'
IF CHARINDEX('-' + right('00' + CAST(MONTH('2/13/2011') AS VARCHAR), 2) + '-', #month) > 0
PRINT 'exists'
ELSE
PRINT 'does not exist'
I don't know what variation on the SQL language you are using, since SQL does not support variables and IF statements, but why not use a variable to represent the result of the condition?
SELECT #MyResult=charindex(str(#cur_month), #month);
-- Now you can write an IF statement using the variable
IF #MyResult...
The return value for select charindex is an integer, not a boolean type. You'll need to put a boolean into your if statement.
Try something like:
If ( select charindex(str(#cur_month), #month) > 0)
Instead of using string functions, use date functions since you are trying to compare one datefield to another. And use datetime for #month instead of varchar:
DECLARE #month datetime;
set #month ='09-03-05';
IF MONTH(#month) = MONTH(CURRENT_TIMESTAMP)
PRINT 'Same Month'
UPDATE
Based on your updated question, here is new code:
DECLARE #month VARCHAR(50);
set #month ='03-05-02-09';
IF CHARINDEX(CAST(MONTH(CURRENT_TIMESTAMP) AS VARCHAR), #month) > 0
PRINT 'Same Month'
This solution pads a zero to the left for months < 10, to make sure it works for February (2) when december is on the list (12), as it was pointed out by OCary
DECLARE #month varchar(50);
set #month ='01-05-07-09-11-12';
If (charindex(right('0' + CAST(MONTH(getdate()) as varchar), 2), #month) > 0)
PRINT 'Same Month'
if you run this:
select right('0' + CAST(MONTH(getdate()) as varchar), 2)
it returns 09

convert strings of type mm/dd/yyyy h to date time

I have a function which converts all of the strings to datetime.
ALTER FUNCTION formatit(
#fromtime VARCHAR(50) -- varchar
)
RETURNS DATETIME
AS
BEGIN
DECLARE #from datetime
IF (CHARINDEX('NOON',#fromtime,0)) = 0
SET #from = CONVERT(DATETIME, #fromtime)
ELSE
SET #from =CONVERT(DATETIME, '01/01/2000 12pm')
RETURN(#from)
END
SELECT dbo.formatit('04/12/2011 12 ')
So when u see the last select stmt it throws an error saying:
The conversion of a char data type to a datetime data type
resulted in an out-of-range datetime value.
It works fine if i give time 4 pm or 4:00. But it gives an error if i give just 4. Kindly let me know how i can handle this?
You could add a lot of code to look at the string and see if there is a ':' (like you have code to look for NOON). The problem is that if the string ends in 12PM the conversion will work too. So you would have to do something like
ALTER FUNCTION formatit(
#fromtime VARCHAR(50) -- varchar
)
RETURNS DATETIME
AS
BEGIN
DECLARE #from datetime
IF (CHARINDEX('NOON',#fromtime,0)) <> 0
BEGIN
SET #from =CONVERT(DATETIME, '01/01/2000 12pm')
END
ELSE IF (CHARINDEX('PM',#fromtime,0)) = 0 AND (CHARINDEX('AM',#fromtime,0)) = 0 AND (CHARINDEX(':',#fromtime,0)) = 0
BEGIN
SET #fromtime = #fromtime+':00'
SET #from =CAST(#fromtime AS datetime)
END
ELSE
BEGIN
SET #from =CAST(#fromtime AS datetime)
END
return(#from)
END
It looks like whitespace is ignored - I tried these and they all worked...
SELECT dbo.formatit('04/12/2011 12 ')
SELECT dbo.formatit('04/12/2011 12:00')
SELECT dbo.formatit('04/12/2011 12PM')
SELECT dbo.formatit('04/12/2011 12AM')
SELECT dbo.formatit('NOON')
Really you should look at the data you are using to feed this function with something other than TSQL so you have a better string parser.
Your call to dbo.formatit('04/12/2011 12 ') results in a call to convert(datetime, '04/12/2011 12 '). This string is not a valid input to convert to a date/time value.
See the list of formats in the CAST and CONVERT reference in MSDN. If you need to convert this string, you will need to change the time portion into "12:00PM" or just "12:00" (which is assumed to be in the 24-hour format).
First, I would find all the unique time occurrences in your table, something like:
select distinct rtrim(substring(#timeField,charindex(' ',#timeField)+1,99))
as TimePortion
This will give you a sense of what you are dealing with.
Then, in your function, break apart the date and time portions...
Now, based on what you found from the query, you need to spell out what to do with the times you find. For example, if the time portion is numeric, you might simply append :00 to it (i.e. 4 becomes 4:00). If you see NOON, you might replace it with 12:00pm, etc.
ALTER FUNCTION formatit( #fromtime VARCHAR(50) )
RETURNS DATETIME
AS
BEGIN
declare #dtPortion VARCHAR(12)
declare #tmPortion VARCHAR(32)
declare #x INT
SET #x = charindex(' ',#fromTime)
SET #dtPortion = left(#fromTime,#x-1)
SET #tmPortion = rtrim(substring(#fromTime,#x+1,99))
-- Figure out what to do
SELECT #FromTime =
CASE
WHEN IsNumeric(#tmPortion) = 1 THEN #dtPortion+' '+#tmPortion+':00'
WHEN #tmPortion = 'NOON' THEN #dtPortion+' 12:00pm'
ELSE
#dtPortion+' 12:00pm'
END
SET #from =CONVERT(DATETIME, '01/01/2000 12pm')
RETURN(#from)
END
Go
Hope this gets you started