Issue with dynamic query and datepart SQL - sql-server

I'm getting the error
Invalid parameter 1 specified for dateadd.
when I try to execute the following dynamic parametrized query in SQL Server 2012:
DECLARE #Interval nvarchar(5) = 'DAY'
DECLARE #Increment int = 10
DECLARE #BaseDate date = getdate()
DECLARE #ResultDate date
DECLARE #Query nvarchar(2000)
SET #Query = 'SELECT #result = DATEADD(#Interval, #Increment, CAST(#BaseDate AS DATE))'
EXECUTE sp_executesql #Query,
N'#result date OUTPUT, #Interval varchar(50), #Increment int, #BaseDate date',
#Interval = #Interval, #Increment = #Increment,
#BaseDate = #BaseDate, #result = #ResultDate OUTPUT
SELECT #ResultDate
I've changed the SET #Query line to this one.
SET #Query = 'SELECT #result = DATEADD(' + #Interval +', #Increment, CAST(#BaseDate AS DATE))'
Although it works fine, I'm curious about why the first statement is causing the error in my dynamic SQL query?. Doesn't sp_executesql generate the same statement than the concatenated query one?

So the way to think about parameterized dynamic sql is you can only use a parameter where you could if it were static SQL. DATEADD expects a special date part keyword (e.g. day, hour, year, etc), not a literal string, and not a variable. It's the same issue some people run into where they think they can parameterize something like a table name. The first statement fails because even in static sql, this is invalid:
declare #increment nvarchar(5) = 'day'
select dateadd(#increment, 1, getdate())
That's equivalent to
select dateadd('day', 1, getdate())
The second statement succeeds because you're concatenating the string "day" which gets evaluated to the keyword.

In the first case, the query (with #Interval expanded to its value) becomes this:
SELECT #result=DATEADD('DAY', #Increment, CAST(#BaseDate AS DATE))
and in the second query it becomes this:
SELECT #result=DATEADD(DAY, #Increment, CAST(#BaseDate AS DATE))
The first query is invalid because there the first parameter to DATEADD is a string value, where the compiler expects a language keyword, and those are not the same in SQL.
For more info, see here: https://learn.microsoft.com/en-us/sql/t-sql/functions/dateadd-transact-sql
Please note the line under datepart saying User-defined variable equivalents are not valid. In other words you can't put quotes around these "values", they are not strings but keywords, and they can not be put in variables.

Related

Why does T-SQL print statement cause ResourceClosedError: This result object does not return rows. It has been closed automatically

I'm calling a SQL Server SP from Jupyter, and the SP looks like this:
ALTER
procedure [dbo].[proc_Report_QuarterlyDistribution02] (#quarter int, #year int, #group int)
as
declare #total int,
#date date
set #date = cast(#year as varchar(4)) + '-01-01'
set #date = dateadd(quarter, #quarter - 1, #date)
print #date
select #total = count(1)
from DimMedical
where ServiceDate between
DATEADD(quarter, -9,#date) and #date
and carriercode = #group
and Category = 'Physicians'
The SP goes on - that's not the issue.
The problem is the line
print #date
Question Why would the print statement cause the error:
ResourceClosedError: This result object does not return rows. It has been closed automatically.
Why would the print statement cause the error: "This result object does not return rows"
This is probably a limitation in the client library you are using. Some client libraries stop looking for a result set when they see a message.
Either remove the print statement, upgrade your client library (not mentioned), or have the stored procedure insert into a table using INSERT … EXEC, and then select from that in a subsequent query.

Reading a string expression with parameters and executing the expression as date

I am trying to create dynamic code for date calculation in SQL stored procedures but am having problems executing string expressions and parameters as date expressions.
I want to hold generic string expressions in a table that will create the dates according to the value of the parameters.
for example this is a generic expression :
DATEADD(#TimeResolution, -#IterationN, #CurrentCalc)
as you can see these generic expressions are composed out of parameters to.
in the stored procedures I intend to declare the variables that are in the expression and assign values to them using a select statement from a different table.
the problem is that after deriving these string values and writing the expression it does not give me the date I want but fails.
so for example if I write the following code
declare #Today date
declare #LastYear date
set #Today = getdate()
set #LastYear = DATEADD(year, -1, #Today)
select #Lastyear
it works fine and I will get last year's date.
but when I try something like this :
declare #Today date
declare #LastYear date
declare #Timeresolution varchar(5)
select #Timeresolution = [Timeresolution] from dbo.mytable where rule_id=1//Timeresolution is a varchar column in my table holding the values 'year' or 'month'
declare Iteration int
select #Iteration = [Iteration] from dbo.mytable where rule_id=1 //Iteration is a int column in my table holding the values 1 or 2, or 3
set #Today = getdate()
set #LastYear = DATEADD(Timeresolution , -Iteration , #Today)
select #Lastyear
this gives me a conversion error.
Is there a way to create such dynamic date expressions?
It isn't possible to use a variable for the interval in DATEADD, but you can do something like this:
IF #Timeresolution = 'year'
BEGIN
SET #LastYear = DATEADD(year, -#Iteration , #Today)
END
IF #Timeresolution = 'month'
BEGIN
SET #LastYear = DATEADD(month, -#Iteration , #Today)
END

What type is INTERVAL in the function DATEPART?

The code select DATEPART(month,getutcdate()) returns an integer representing the numeric month value of the current date.
The value MONTH is one of a number of predefined options for the interval parameter.
What is the underlying data type of that interval value?
declare #date datetime = getutcdate()
, #interval notNVarChar(16) = 'month' --what type should this be?
--Would it need quotes for assignment?
select DATEPART(#interval, #date)
You can make it parameterize by making a dynamic SQL as below:
declare #date datetime
set #date = GETDATE()
declare #option varchar(50)
declare #sql varchar(max)
SET #option = 'MONTH' --Here you can set other options like hour, second, milisecond etc..
set #sql = 'SELECT DATEPART('+ #option + ',''' + CONVERT(varchar,#date,21)+''')'
print #sql
EXEC( #sql)
There isn't one - you cannot parameterize it.
From the documentation:
DATEPART ( datepart , date )
datepart
Is the part of date (a date or time value) for which an integer will be returned. The following table lists all valid datepart arguments. User-defined variable equivalents are not valid.
(my emphasis)
There are lots of options available like :
day
week
hour
minute
second
millisecond
etc..
PLease go thru thisLINK

how to pass European date as parameter to stored procedure

Using SQL Server 2012, and the new date type (not dateTime) I created the next procedure:
CREATE PROC Test(#StartDate date ,#EndDate date)
AS
DECLARE #Temp TABLE([ID] int, [Date] date)
INSERT INTO #Temp SELECT 1, CONVERT(date,'31/12/2012',103)
INSERT INTO #Temp SELECT 2, CONVERT(date,'01/10/2012',103)
INSERT INTO #Temp SELECT 3, CONVERT(date,'01/01/2012',103)
SELECT * FROM #Temp WHERE [Date] BETWEEN #StartDate AND #EndDate
When I run this stored procedure I get the next error indicating the date format of the parameter:
Incorrect syntax near '/'.
Please what should be done ?
For one, your date shouldn't have a / in it. But it also needs to be enclosed in quotes. Try:
EXEC dbo.Test #StartDate = '20120101', #EndDate = '20120131';
The reason it needs to be enclosed in quotes is, if you don't use quotes, your "date" is interpreted as a mathematical expression, e.g.:
01/01/2012 = 1 divided by 1 divided by 2012
The reason your date shouldn't have a / in it is because m/d/y and d/m/y are unsafe formats that can be interpreted differently depending on language and other settings.
And finally, if you are calling this procedure from C#, why are you passing strings and not properly typed parameters?

T-SQL - get date by dynamic params

I tried to figure out a dynamic query to get date col within past 20 days. The idea is quite simple and, moreover, I know the table does contain dates from getdate() to -20 days but still no result get returned
DECLARE #date_past_period varchar(MAX);
DECLARE #date_past_number varchar(MAX);
SET #date_past_period='day';
SET #date_past_number='20';
DECLARE #aDate datetime;
DECLARE #sql varchar(MAX);
SET #sql='SELECT date FROM table WHERE convert(varchar,date,121) BETWEEN convert(varchar,getdate(),121) AND convert(varchar,dateadd('+#date_past_period+', -'+#date_past_number+', getdate()),121)';
exec(#sql);
Maybe the problem is in dynamic thing but I am not sure.
Any useful comment is appreciated
You can use CASE function (T-SQL):
CREATE PROCEDURE MyStoredProcedure
#IntervalType VARCHAR(15),
#Num INT
AS
DECLARE #StartDate DATETIME = GETDATE();
DECLARE #EndDate DATETIME =
CASE #IntervalType
WHEN 'DAY' THEN DATEADD(DAY,#Num,#StartDate)
WHEN 'MONTH' THEN DATEADD(MONTH,#Num,#StartDate)
WHEN 'YEAR' THEN DATEADD(YEAR,#Num,#StartDate)
END;
IF #EndDate IS NULL
RAISERROR('Invalid params', 16, 1);
ELSE
SELECT date FROM table WHERE date BETWEEN #StartDate AND #EndDate;
By converting to VARCHAR your search condition from WHERE will not be SARG ( 1 & 2 ).
I am pretty sure this scenario can be covered without using dynamic SQL, however, one obvious problem in your SQL is the between clause - the range is in the wrong order. Try changing your #sql as below:
SET #sql='SELECT date FROM table WHERE convert(varchar,date,121) BETWEEN convert(varchar,dateadd('+#date_past_period+', -'+#date_past_number+', getdate()),121) AND convert(varchar,getdate(),121)';

Resources