Why datetime is not apply to the parameter in SQL Server? - sql

I have a application runs this T-SQL command:
exec sp_executesql #statement = N'mysp #timestamp=0',
#params = N'#0 datetime',
#0 = '2021-04-08 20:59:10.987'
I log the timestamp value and I get empty for some reason:
1900-01-01 00:00:00.000
This is the stored procedure code:
ALTER PROCEDURE [dbo].[mysp]
#TimeStamp DATETIME = NULL
AS
SET NOCOUNT ON
DECLARE
....
BEGIN TRY
SELECT #TimeStamp
...
What is wrong in the T-SQL command? How should it be in this structure?

What I see is procedure named mysp which expects one parameter named #timestamp. The =0 part sets the value of this parameter to 0. It is not the name of another paramter. When interpreted as a datetime, a 0 value matches the 1900-01-01 you observed.
I expect this is what the application is trying to do:
exec sp_executesql #statement = N'mysp #timestamp',
#params = N'#timestamp datetime',
#timestamp = '2021-04-08 20:59:10.987'
Alternatively:
exec sp_executesql #statement = N'mysp #timestamp=#0',
#params = N'#0 datetime',
#0 = '2021-04-08 20:59:10.987'
In communicating with developers about how to fix this on their end, keep in mind they are probably not writing the exec sp_executesql code directly. Rather, they are more likely using a tool to just call mysp that alters the code to use sp_executesql behind the scenes for them. This is common practice as a safe way for developers to use parameterized queries in their code and avoid Sql Injection security issues.

Related

EXEC sp_executesql will work with Integers but not VarChars

I'm using EXEC sp_executesql for a dynamic query in SQL Server 2017.
I've tried various testing scenarios, and I can get results in my query (for other parameters) as long as the values passed in are Integers. So, that means, Location and Department testing works. However, I can't figure out if there's something I need to do differently for when I'm sending a NVARCHAR or DateTime.
Here's my stored procedure, with the NVARCHAR param. Do you see anything I'm doing wrong?
(
#tktitle NVARCHAR(200)
)
AS
BEGIN
Declare #SQL NVARCHAR(MAX)
Set #SQL = 'SELECT timekeep.tkinit, timekeep.tkfirst, timekeep.tklast,
timekeep.tkemdate, timekeep.tktitle, timekeep.tkloc, timekeep.tkdept
FROM abc.xyz'
IF #tktitle IS NOT NULL
Select #SQL = #SQL + 'AND ([tktitle] = #tktitle)'
EXEC sp_executesql #SQL, N'#tktitle varchar', #tktitle
END
I can identify at least three issues:
You need to specify a length for varchar when passing it as a parameter.
You also need a space before the AND and the AND should be a WHERE.
You need to assign the parameter in the execute call.
So:
IF #tktitle IS NOT NULL
Select #SQL = #SQL + ' WHERE ([tktitle] = #tktitle)';
-------------------------^ separator
EXEC sp_executesql #SQL, N'#tktitle varchar(200)', #tktitle=#tktitle;

Calling Scalar Function on a linked Server

I am trying to call a scalar function on a linked server but I am having a little trouble setting it up. I am hoping to set it up as a function on my server.
Below is the best I came up with.
I am trying to wrap an openquery statement within the function on my server. However, the query works by itself by I am not able to return the results without causing an error.
USE POWERVIEW
GO
ALTER FUNCTION DBO.FN_VAR_DUMPNAME (#DUMPLOC NVARCHAR(40))
RETURNS NVARCHAR(40)
AS
BEGIN
--DECLARE #DUMPLOC NVARCHAR(40)='D11'
DECLARE #sql nvarchar(800)
DECLARE #param Nvarchar(20)= #DUMPLOC
DECLARE #retval NVARCHAR(40)
DECLARE #ParmDefinition nvarchar(500)=N'#retvalOUT NVARCHAR(40) OUTPUT'
DECLARE #innersql nvarchar(400)
SET #innersql = 'SELECT POWERVIEW.DBO.FN_VAR_DUMPNAME('''+''''+#param +''''+''')'
SET #sql = 'select * from openquery(MINESQLSERVER,'''+ #innersql +''' )'
***RETURN EXEC sp_executesql #sql --This line does not work***
END
This is too long for a comment.
SQL Server does not allow functions to call dynamic SQL. Hence you cannot do what you want.
You have other problems as well:
return exec is not something I've every seen before.
exec returns an integer.
The function returns a string.
You will need to solve your problem using some other method -- a stored procedure comes to mind.

Dynamically get all parameter values in stored procedure

Is there any way to get all parameter values from a stored procedure dynamically?
In other words, iterate through all parameters in one stored procedure to get their values into one string. This is for a unified logging process for a bunch of stored procedures.
I can get the names of parameters:
SELECT PARAMETER_NAME
FROM INFORMATION_SCHEMA.PARAMETER
WHERE SPECIFIC_NAME = 'procedure_name';
Also, I tried to use dynamic SQL commands. I've generated a command with included parameter, but EXEC can't execute command.
#cmd = 'SELECT '#UserID' + CONVERT(NVARCHAR(MAX), #UserID)
+ '#Date' + CONVERT(NVARCHAR(MAX), #Date)'
EXEC #cmd
Is there any way to do this besides manually generating a list of parameter values for each stored procedure?
Since SQL Server 2014 there is sys.dm_exec_input_buffer a table valued function with an output column event_info that gives the full execution statement (including parameters).
I use this for error logging in stored procedures.
For example:
--include this inside the stored procedure
declare #statement nvarchar(max)
select #statement = event_info
from sys.dm_exec_input_buffer(##spid, current_request_id())
--this will print whatever you called the procedure with (including parameters)
print #statement
-- if you want to parse just the parameters from the statement, it can be done like this
declare #proc_name varchar(128) = object_name(##procid)
declare #param_idx int = charindex(#proc_name, #statement) + len(#proc_name)
declare #param_len int = len(#statement) - #param_idx
declare #params nvarchar(max) = right(#statement, #param_len)
select #params

Using variable value in string when executing EXEC in SQL

I want to use a variable value in exec where i don't need to create the query itself.
I will have a query stored in a field in my database and i just want to execute that using the parameters in that stored procedure. For Example below i declared two variables #ValueVariable is the parameter of stored procedure and what i declared #QueryString is the one i will read from data base and i want to execute that using the value of #ValueVariable.
DECLARE #ValueVariable int=0
#QueryString VARCHAR(MAX)=
'SELECT UserName FROM TableUser WHERE UserId=#ValueVariable'
EXEC(#QueryString)
When i try to execute that i get an error Incorrect syntax near 'SELECT UserName FROM TableUser WHERE UserId=#ValueVariable'
I am aware that i can do it by
#QueryString VARCHAR(MAX)=
'SELECT UserName FROM TableUser WHERE UserId='+#ValueVariable
But i want to use it as stated above. Not making a query in my procedure but using variable value as in string retrieved from DB.
So is there any way i could be able to execute that using the value from the variable in current environment.
You can use sp_executesql.
DECLARE
#IntVariable int,
#SQLString nvarchar(500),
#ParmDefinition nvarchar(500)
SELECT
#IntVariable = 0,
#SQLString = N'SELECT UserName FROM TableUser WHERE UserId=#ValueVariable',
#ParmDefinition = N'#ValueVariable INT'
SP_EXECUTESQL
#SQLString,
#ParmDefinition,
#ValueVariable = #IntVariable;
In essence, it creates a one time stored procedure. The #paramDefinition variable is the parameter signature you'd normally see in a stored procedure, the sql server caches the execution plan, etc, etc.

Fully qualified table names with SP_ExecuteSql to access remote server

Trying to update a table on a linked server (SQL 2000/2005) but my server name will not be known ahead of time. I'm trying this:
DECLARE #Sql NVARCHAR(4000)
DECLARE #ParamDef NVARCHAR(4000)
DECLARE #SERVER_NAME VARCHAR(35)
SET #Sql = 'UPDATE
#server_name_param.dba_sandbox.dbo.SomeTable
SET SomeCol=''data'''
SET #ParamDef = N'#server_name_param VARCHAR(35)'
print #Sql
exec sp_executesql #Sql, #ParamDef, #server_name_param=#SERVER_NAME
Which returns this:
UPDATE
#server_name_param.dba_sandbox.dbo.SomeTable
SET SomeCol='data'
Msg 170, Level 15, State 1, Line 2
Line 2: Incorrect syntax near '.'.
Any ideas? Is there anyway I view the SQL statement that is being executed after the parameters are bound?
You'll have to do this, it can't be parameterised
....
SET #Sql = 'UPDATE ' + #server_name_param + '.dba_sandbox.dbo.SomeTable SET SomeCol=''data'''
....
Edit: There is another way which I used back in my pure DBA days
EXEC sp_setnetname 'AdhocServer', #SERVER_NAME
UPDATE AdhocServer.dba_sandbox.dbo.SomeTable SET SomeCol 'data'
EXEC sp_setnetname 'AdhocServer', 'MeaninglessValue'
sp_setnetname is there from SQL Server 2000 to 2008
Edit2. Permissions:
Try EXECUTE AS LOGIN = 'login_name' , where login_name is a superuser
I've not really used this (I use "AS USER" for testing), so not sure of the finer points...
Edit 3: for concurrency, consider using sp_getapplock and a stored procedure, or some other concurrency control mechanism.
You cannot do this with parameters directly - you would have to use dynamic SQL, or send the server name as a parameter to an SP that does dynamic SQL:
DECLARE #template NVARCHAR(4000)
DECLARE #Sql NVARCHAR(4000)
DECLARE #SERVER_NAME VARCHAR(35)
SET #template = 'UPDATE {#server_name_param}.dba_sandbox.dbo.SomeTable SET SomeCol=''data'''
SET #sql = REPLACE(#template, '{#server_name_param}', #SERVER_NAME)
print #Sql
exec sp_executesql #Sql -- OR EXEC ( #sql )
I like gbn's trick. I didn't know that one and I'm gonna have to research that some more.
Since I didn't know that trick, I've had to use dynamic sql in similar situations in the past (like what Cade posted). When that happens I would normally query an information schema view to make sure the parameter value is a real database object before building the query. That way I'm sure it's not an injection attempt.