Executing dynamic query inside a scalar function sql server - sql-server

I am trying to make a scalar function in SQL Server. I have coined up the select statement as follows:
SET #Statement1 = 'SELECT 1 FROM ' + #p_TableName +
' WHERE RegionTypeID = 1 AND RegionID = ' +
CONVERT (nvarchar (50), #p_RegionID)
Here #p_TableName is the name of the table (nvarchar (500)) and #p_RegionID is a uniqueidentifier.
I am now executing the above statement as follows:
EXEC #ReturnValue1 = #Statement1
WHERE #ReturnValue1 is int.
But when I call this function from elsewhere I get this error:
The name 'SELECT 1 FROM [PS].[Availability] WHERE RegionTypeID = 1 AND
RegionID = AF4C182C-F751-41AD-AA6A-20A50A7A38C8' is not a valid
identifier.
I need to know how I can call a dynamic sql select statement from within a scalar function.

You can create SP like below with output parameter:
CREATE PROCEDURE ProcNameGoesHere
#p_TableName nvarchar(500),
#p_RegionID uniqueidentifier,
#output int OUTPUT
AS
BEGIN
DECLARE #Statement1 nvarchar(max),
#ParmDefinition nvarchar(500)
SET #Statement1 = N'SELECT #someValue = 1 FROM ' + #p_TableName +
' WHERE RegionTypeID = 1 AND RegionID = ' +
CONVERT (nvarchar (50), #p_RegionID)
SET #ParmDefinition = N'#someValue int OUTPUT'
EXEC sp_executesql #Statement1, #ParmDefinition, #output=#someValue OUTPUT
RETURN;
END
Then you can call it like this:
DECLARE #output int
EXEC ProcNameGoesHere '[PS].[Availability]','AF4C182C-F751-41AD-AA6A-20A50A7A38C8', #output OUTPUT
SELECT #output
Or:
IF ISNULL(#output,0) = 1
BEGIN
--Do something here
END

Related

Must declare the scalar variable error for stored procedure in nopcommerce 4.3

I want to search GTIN option in admin product list. So, for that I am providing GTIN value in ProductLoadAllPaged store procedure. Now, when I search GTIN value from product list at that time throw datatable error and from console application get message that System.Data.SqlClient.SqlException (0x80131904): Must declare the scalar variable "#GTIN"..
Here is store procedure added code,
ALTER PROCEDURE [dbo].[ProductLoadAllPaged]
(
#GTIN nvarchar(50) = null--AWAZ
)
AS
BEGIN
...........
--SKU (exact match)
IF #SearchSku = 1
BEGIN
SET #sql = #sql + 'OR p.[Sku] = #OriginalKeywords '
END
--NEW ADDED CODE FOR GTIN SEARCH
IF #GTIN is not null
BEGIN
SET #sql = #sql + 'AND p.Gtin = #GTIN'
END
--localized product name
SET #sql = #sql + '
UNION
SELECT lp.EntityId
FROM LocalizedProperty lp with (NOLOCK)
WHERE
lp.LocaleKeyGroup = N''Product''
AND lp.LanguageId = ' + ISNULL(CAST(#LanguageId AS nvarchar(max)), '0') + '
AND ( (lp.LocaleKey = N''Name'''
..........
END
In the stored procedure change this line of code:
EXEC sp_executesql #sql, N'#Keywords nvarchar(4000), #OriginalKeywords nvarchar(4000)', #Keywords, #OriginalKeywords
To this:
EXEC sp_executesql #sql, N'#Keywords nvarchar(4000), #OriginalKeywords nvarchar(4000), #GTIN nvarchar(50)', #Keywords, #OriginalKeywords, #GTIN
This will add your new parameter when executing the dynamic SQL query.

Call a Stored Procedure or Function from Dynamic SQL - with Nullable Parameter

I am trying to call an sql function accepting a nullable parameter - from a dynamic SQL statement.
Creating the dynamic statement is difficult because when the parameter value is 'NULL' the concatentation causes the whole statement to be empty. I have the following:
SET dynamicQuery =
'select * from [qlik].udf_getStatistic( ''' + #myParameter + ''' )'
The sample above is inside a stored procedure to which #myParameter is passed. It may be null, or a string value. Clearly, when it is a string it needs to be enclosed in quotes, but when it is null it must not be enclosed in quotes. As follows:
select * from [qlik].udf_getStatistic( 'Heights' )
select * from [qlik].udf_getStatistic( NULL )
The question is equally applicable to calling a stored procedure accepting a nullable parameter from dynamic SQL.
The examples are from SQL Server.
Just escape the NULL value with an explicit literal NULL, making sure that the quotes are only included when the value is not NULL.
DECLARE #myParameter VARCHAR(10) = 'ABC'
DECLARE #dynamicQuery VARCHAR(MAX)
SET #dynamicQuery =
'select * from [qlik].udf_getStatistic(' + ISNULL('''' + #myParameter + '''', 'NULL') + ')'
SELECT #dynamicQuery -- select * from [qlik].udf_getStatistic('ABC')
SET #myParameter = NULL
SET #dynamicQuery =
'select * from [qlik].udf_getStatistic(' + ISNULL('''' + #myParameter + '''', 'NULL') + ')'
SELECT #dynamicQuery -- select * from [qlik].udf_getStatistic(NULL)
You might want to escape additional single quotes that might be on your variable, replacing them with double single quotes, so it doesn't break your dynamic build.
The answer is actually different between stored procedures and functions.
From Books On Line or whatever they call it this month (Scroll down a ways):
When a parameter of the function has a default value, the keyword DEFAULT must be specified when the function is called to retrieve the default value. This behavior is different from using parameters with default values in stored procedures in which omitting the parameter also implies the default value. However, the DEFAULT keyword is not required when invoking a scalar function by using the EXECUTE statement.
So for a proc, when you want to pass a NULL parameter, you can just not pass it. For a function, though, you have to tell it to use the DEFAULT value explicitly. Either way, you do not pass it an explicit NULL. Luckily for your dynamic SQL, though, the explicit DEFAULT also works with a stored procedure. In both cases, in order to make sure that the parameters you are passing get assigned correctly, you want to use explicit parameter names in your call.
Let's use this function definition:
CREATE FUNCTION (or procedure) [qlik].udf_getStatistic (
#param1 integer = 0,
#param2 varchar(100) = 'foo'
) AS ...
Both parameters are optional. Since this is a function, this call will throw an insufficient number of parameters error:
select * from [qlik].udf_getStatistic( 'Heights' );
If it were a procedure call, it would throw a cannot convert value 'Heights' to data type integer because it will apply the only parameter value passed to the first parameter it encounters, which is expecting an integer. In both cases, you get what you want this way:
select * from [qlik].udf_getStatistic( #param1 = DEFAULT, #param2 = 'Heights' );
Which brings us to your dynamic SQL. Add your parameter name(s) to the static text, then use COALESCE (or CASE if you like) to decide whether to pass an explicit value, or the DEFAULT call.
DECLARE #myParameter1 VARCHAR(100) = 'foo',
#myParameter2 INTEGER,
#SQL NVARCHAR(MAX);
SET #SQL =
'select
*
from [qlik].udf_getStatistic(
#param1 = ''' + COALESCE(#myParameter1, 'DEFAULT') + ''',
#param2 = ' + COALESCE(CAST(#myParameter2 AS VARCHAR(30)),'DEFAULT') + ' );';
SELECT #SQL;
Result:
select * from [qlik].udf_getStatistic( #param1 = 'foo', #param2 = DEFAULT );
From my understanding, I try this on SQL Server 2012,
CREATE PROCEDURE ToNullProc
(#i VARCHAR(20))
AS
BEGIN
PRINT 'you entered ' + #i
END
CREATE FUNCTION ToNullFun
(#i VARCHAR(20))
RETURNS #table TABLE (i VARCHAR(20))
AS
BEGIN
INSERT INTO #table
SELECT ('You entered ' + #i) a
RETURN
END
DECLARE #j VARCHAR(20) = 'Hi',
#QueryFun NVARCHAR(50) = N'',
#QueryProd NVARCHAR(50) = N''
IF #j IS NOT NULL
BEGIN
SET #QueryFun = N'select * from ToNullFun ('''+#j+''')'
SET #QueryProd = N'exec ToNullProc '''+#j+''''
END
ELSE BEGIN
SET #QueryFun = N'select * from ToNullFun ('+#j+')'
SET #QueryProd = N'exec ToNullProc '+#j+''
END
PRINT #queryfun
PRINT #queryprod
EXEC sp_executesql #queryfun
EXEC sp_executesql #queryprod
update for dynamic procedure and dynamic function :
create table #temp (Num int identity (1,1), NullVal int)
insert into #temp (NullVal) values (1),(null),(3)
alter proc ToNullProc (
#Operator varchar (max), #NullVal varchar (max)
) as
begin
declare #Query nvarchar (max) = N'select * from #temp where NullVal ' +
#Operator + #NullVal
-- print #query + ' ToNullProc print '
exec sp_executesql #query -- Here we run the select query from Proc
end
create function ToNullFun (
#Operator varchar (max), #NullVal varchar (max)
)
returns nvarchar (max)
as
begin
declare #Query nvarchar (max)
set #Query = N'select * from #temp where NullVal ' + #Operator + #NullVal
/*
I try to into to Table variable by using ITVF,
'insert into #table exec sp_executesql #query'.
But this type of insert is not allowed in ITVF.
*/
return #query
end
declare #NullVal varchar (max) = '1'
, #QueryFun nvarchar (max) = N''
, #QueryProd nvarchar (max) = N''
declare #FunResultTalbe table (
Query nvarchar (100)
) /* To store the result Funtion */
if #NullVal is not null
begin
set #QueryFun = N'select dbo.ToNullFun ('' = '','''+#NullVal+''')'
set #QueryProd = N'exec ToNullProc '' = '','''+#NullVal+''''
end
else begin
set #QueryFun = N'select dbo.ToNullFun ('' is null '','''')'
set #QueryProd = N'exec ToNullProc '' is null '','''''
end
print #queryfun + ' At start'
print #queryprod + ' At start'
exec sp_executesql #queryprod -- It calls Proc
insert into #FunResultTalbe
exec sp_executesql #queryfun -- It calls the Function and insert the query into the table.
set #QueryFun = (select top 1 * from #FunResultTalbe) -- Here we get the query from the table.
print #queryfun
exec sp_executesql #queryfun -- Here we run the select query. Which is dynamic
Result sets
-- Result of Procedure
Num NullVal
1 1
-- Result of Function
Num NullVal
1 1
Let me know, what did you got.

using Dynamic SQL - Custom Universal Multi-Action Stored Procedure (with TVPar)

this is the stored procedure i have started to write
the problem is i couldn't figure out how to handle the part of reading values from the TV par :
Must declare the table variable "#TestMultiActionViaRowIndexTVPar".
ALTER Proc [dbo].[MultiActionViaRowIndexSpTVP]
#SelectedSDTOName varchar (50), #SelectedAction varchar(10), #TestMultiActionViaRowIndexTVPar dbo.TestMultiActionViaRowIndexTVType READONLY
as BEGIN
declare #CmdStr varchar(500) = '';
if(#SelectedAction = 'SELECT') Begin --test for one of the "Action" types
SET #CmdStr = 'SELECT * FROM ' + #SelectedSDTOName +
' WHERE RowIndex in (SELECT RowIndex FROM #TestMultiActionViaRowIndexTVPar)'
End
--else - other Action - #CmdStr will be according to action...
--finally execute constructed Cmdstr
Exec(#CmdStr);
END
Use sp_executesql to pass a TVP or any other parameter to parameterized dynamic SQL:
ALTER PROC [dbo].[MultiActionViaRowIndexSpTVP]
#SelectedSDTOName varchar (50)
, #SelectedAction varchar(10)
, #TestMultiActionViaRowIndexTVPar dbo.TestMultiActionViaRowIndexTVType READONLY
AS
DECLARE #CmdStr nvarchar(MAX);
IF #SelectedAction = 'SELECT'
BEGIN
SET #CmdStr = 'SELECT * FROM ' + #SelectedSDTOName +
' WHERE RowIndex in (SELECT RowIndex FROM #TestMultiActionViaRowIndexTVPar);'
END;
EXEC sp_executesql
#CmdStr
,N'#TestMultiActionViaRowIndexTVPar dbo.TestMultiActionViaRowIndexTVType READONLY'
,#TestMultiActionViaRowIndexTVPar = #TestMultiActionViaRowIndexTVPar;
GO

Passing negative values into stored procedure

I am trying to insert ID values in stored procedure from .net and the int value for ID is inserting negative value in the stored procedure.
But when a negative value is passed its giving me an error incorrect syntax near '*'.
Please help me.
Here is my stored procedure
ALTER PROCEDURE [dbo].[HotlinePlusAdministration_ArticleMigrator]
#Id AS INT,
--#CategoryID AS INT,
--#Title AS Varchar(200),
--#ArticleDate AS datetime,
#DestLinkServer AS VARCHAR(50),
#UserID AS VARCHAR(8),
#ReturnMsg AS VARCHAR(1000) OUTPUT
AS
BEGIN
DECLARE #Query AS NVARCHAR(4000)
DECLARE #Log AS VARCHAR(8000)
DECLARE #ArticleID as int
DECLARE #NewArticleID as int
DECLARE #ArticleKeyExists as int
DECLARE #Title as varchar(200)
DECLARE #CategoryID as INT
DECLARE #ArticleDate as varchar(30)
DECLARE #ParmDefinition nvarchar(500);
SET XACT_ABORT ON -- Required for nested transaction
BEGIN TRAN
-- Check if ArticleID exists in Destination Server
SET #Query = N' SELECT #ArticleKeyExists = COUNT(*)
FROM ' + #DestLinkServer + '.HL2_61.dbo.Article' + ' where ArticleKey = ' + str(#Id)
SET #ParmDefinition = N' #ID int, #ArticleKeyExists int OUTPUT';
EXECUTE sp_executesql #Query , #ParmDefinition, #ID, #ArticleKeyExists OUTPUT;
--EXECUTE sp_executesql 1234,'BRHLSQL8','BRWSQLDC',#return = retnmsg
IF ##ERROR <> 0
BEGIN
ROLLBACK TRANSACTION
SET #ReturnMsg = #Log + '<span style="color:red;">ERROR: <br></span>'
RETURN -1
END
--Delete existing Articles for select page
set #Query = 'DELETE FROM ' + #DestLinkServer +
'.HL2_61.dbo.Article ' +
'WHERE ArticleKey = ' + CONVERT(VARCHAR, #Id)
--'WHERE CategoryID = ' + CONVERT(VARCHAR, #CategoryID) + ' and Title = ''' + #Title + ''' and ArticleDate = ''' + #ArticleDate + ''''
Print #Query
EXEC(#Query)
When I am executing the code as below I am getting the error.
DECLARE #return_value int,
#ReturnMsg varchar(1000)
EXEC #return_value = [dbo].[Migrator]
#Id = -1591276581,
#DestLinkServer = N'SQLDC',
#UserID = N'10c1',
#ReturnMsg = #ReturnMsg OUTPUT
SELECT #ReturnMsg as N'#ReturnMsg'
SELECT 'Return Value' = #return_value
GO
Please someone help me..
Thanks
When you convert an int to a varchar, you have to specify the size:
Try this:
CONVERT(VARCHAR(50), #Id)
and avoid using str(#Id)

How do I dynamically build a like clause in an executable sql stored procedure that uses EXEC sp_executesql?

The following stored procedure works correctly execpt when I pass in the #NameSubstring parameter. I know I am not dynamically building the like clause properly. How can I build the like clause when this parameter also needs to be passed as a parameter in the EXEC sp_executesql call near the bottom of the procedure?
ALTER PROCEDURE [dbo].[spGetAutoCompleteList]
(
#AutoCompleteID int,
#StatusFlag int,
#NameSubstring varchar(100),
#CompanyID int,
#ReturnMappings bit,
#ReturnData bit
)
AS
DECLARE #ErrorCode int,
#GetMappings nvarchar(500),
#Debug bit,
#Select AS NVARCHAR(4000),
#From AS NVARCHAR(4000),
#Where AS NVARCHAR(4000),
#Sql AS NVARCHAR(4000),
#Parms AS NVARCHAR(4000)
SET #ErrorCode = 0
SET #Debug = 1
BEGIN TRAN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
IF #AutoCompleteID IS NOT NULL OR #StatusFlag IS NOT NULL OR #NameSubstring IS NOT NULL
BEGIN
SET #Select = '
SELECT ac.AutoCompleteID,
ac.AutoCompleteName,
ac.CompanyID,
ac.StatusFlag,
ac.OwnerOperID,
ac.CreateDT,
ac.CreateOperID,
ac.UpdateDT,
ac.UpdateOperID,
ac.SubmitOperID,
ac.SubmitDT,
ac.ReviewComments'
SET #GetMappings = '
Select ac.AutoCompleteID'
IF #ReturnData = 1
BEGIN
SET #Select = #Select + '
, ac.AutoCompleteData'
END
SET #From = '
FROM tbAutoComplete ac'
SET #Where = '
WHERE 1=1'
IF #AutoCompleteID IS NOT NULL
BEGIN
SET #Where = #Where + '
AND ac.AutoCompleteID = CAST(#AutoCompleteID AS nvarchar)'
END
IF #StatusFlag IS NOT NULL
BEGIN
SET #Where = #Where + '
AND ac.StatusFlag = CAST(#StatusFlag AS nvarchar)'
END
IF #NameSubstring IS NOT NULL
BEGIN
SET #Where = #Where + '
AND ac.AutoCompleteName like #NameSubstring' + '%'
END
SET #Where = #Where + '
AND ac.CompanyID = + CAST(#CompanyID AS nvarchar)'
SET #Sql = #Select + #From + #Where
SET #Parms = '
#AutoCompleteID int,
#StatusFlag int,
#NameSubstring varchar(100),
#CompanyID int'
EXEC sp_executesql #Sql,
#Parms,
#AutoCompleteID,
#StatusFlag,
#NameSubstring,
#CompanyID
IF #ReturnMappings = 1
BEGIN
SET #GetMappings = 'Select * FROM tbAutoCompleteMap acm WHERE acm.AutoCompleteID IN(' + #GetMappings + #From + #Where + ')'
--EXEC sp_executesql #GetMappings
END
IF #Debug = 1
BEGIN
PRINT #GetMappings
PRINT #Sql
END
END
SELECT #ErrorCode = #ErrorCode + ##ERROR
IF #ErrorCode <> 0
BEGIN
SELECT '<FaultClass>1</FaultClass><FaultCode>1</FaultCode>'
+ '<FaultDesc>Internal Database Error.</FaultDesc>'
+ '<FaultDebugInfo>(spGetAutoCompleteList): There was an error while trying to SELECT from tbAutoComplete.</FaultDebugInfo>'
ROLLBACK TRAN
RETURN
END
COMMIT TRAN
#NameString needs to be outside of the quotes. To get #NameString% enclosed in quotes, you use two single quotes to escape the quote character as a literal.
SET #Where = #Where + '
AND ac.AutoCompleteName like ''' + #NameSubstring + '%'''
To avoid SQL injection, do not use concatenation when adding the parameter to your SQL statement. I strongly recommend that you use this format:
IF #NameSubstring IS NOT NULL BEGIN
SET #Where += 'AND ac.AutoCompleteName LIKE #NameSubstring + char(37)'
END
By using char(37) instead of '%' you avoid having to escape the apostrophes around the string literal
If you wanted to put a wildcard at either side, then you would use
IF #NameSubstring IS NOT NULL BEGIN
SET #Where += 'AND ac.AutoCompleteName LIKE char(37) + #NameSubstring + char(37)'
END
-----------------------------------------------------------------------------
In case someone believes I am wrong, here's proof that concatenation is a risk.
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TestInjection]') AND type in (N'U')) BEGIN
create table TestInjection(ID int, Value nvarchar(10))
insert into TestInjection (ID,Value)
Values
(1,'Tom'),
(2,'Fred'),
(3,'Betty'),
(4,'Betty2'),
(5,'Betty3'),
(6,'George')
END
declare #NameSubstring nvarchar(1000) = 'Bet'
--declare #NameSubstring nvarchar(1000) = 'Bet%'';delete from TestInjection;select * from TestInjection where value = ''x'
declare #ID int = 2
Declare #sql nvarchar(1000) = 'select * from TestInjection where ID > #ID '
SET #sql +=' AND [Value] like ''' + #NameSubstring + '%'''
Declare #params nvarchar(100) = '#ID int'
exec sp_executesql #sql, #params, #ID
select * from TestInjection
Run it the first time and you will get a resultset with 3 records, and another with all 6 records.
Now swap the declaration of #NameSubstring to the alternative, and re-run. All data in the table has been deleted.
If on the other hand you write your code like:
declare #NameSubstring nvarchar(1000) = 'Bet'
--declare #NameSubstring nvarchar(1000) = 'Bet%'';delete from TestInjection;select * from TestInjection where value = ''x'
declare #ID int = 2
Declare #sql nvarchar(1000) = 'select * from TestInjection where ID > #ID '
SET #sql +=' AND [Value] LIKE #NameSubstring + char(37)'
Declare #params nvarchar(100) = '#ID int, #NameSubstring nvarchar(1000)'
exec sp_executesql #sql, #params, #ID, #NameSubstring
select * from TestInjection
Then you still get the 3 records returned the first time, but you don't lose your data when you change the declaration.
SET #Where = #Where + 'AND ac.AutoCompleteName like ''%' + #NameSubstring + '%'''
So, you are asking how to specify parameters when you use dynamic queries and sp_executesql ?
It can be done, like this:
DECLARE /* ... */
SET #SQLString = N'SELECT #LastlnameOUT = max(lname) FROM pubs.dbo.employee WHERE job_lvl = #level'
SET #ParmDefinition = N'#level tinyint, #LastlnameOUT varchar(30) OUTPUT'
SET #IntVariable = 35
EXECUTE sp_executesql #SQLString, #ParmDefinition, #level = #IntVariable, #LastlnameOUT=#Lastlname OUTPUT
You can read more about it here: http://support.microsoft.com/kb/262499
Perhaps this wouldn't be an issue if you weren't using dynamic SQL. It looks to me like a vanilla query would work just as well and be much more straightforward to read and debug. Consider the following:
SELECT ac.AutoCompleteID,
ac.AutoCompleteName,
ac.CompanyID,
ac.StatusFlag,
ac.OwnerOperID,
ac.CreateDT,
ac.CreateOperID,
ac.UpdateDT,
ac.UpdateOperID,
ac.SubmitOperID,
ac.SubmitDT,
ac.ReviewComments
FROM tbAutoComplete ac
WHERE ((ac.AutoCompleteID = CAST(#AutoCompleteID AS nvarchar) OR (#AutoCompleteID IS NULL))
AND ((ac.StatusFlag = CAST(#StatusFlag AS nvarchar)) OR (#StatusFlag IS NULL))
AND ((ac.AutoCompleteName like #NameSubstring + '%') OR (#NameSubstring IS NULL))
AND ((ac.CompanyID = CAST(#CompanyID AS nvarchar)) OR (#CompanyID IS NULL))
This is much simpler, clearer etc. Good luck!

Resources