Dynamic sql to populate a stored procedure variable - sql

I want to add a record in a aTable using a stored procedure. But before that I need to check if it's a duplicate entry. If its not duplicate then only add to aTable.
I have one stored procedure that does two things
checking for duplicate
and if not duplicate then only add the record.
In this stored procedure, I have
#count variable (scope is the stored procedure itself)
column values as parameters (7-8 as varchar datatype)
I am setting #count to 1 and then building the dynamic #qry string that will hold the #count value as follows using #column values
CREATE PROCEDURE sp_aTable_ADD
#col1 varchar(20) = null,
#col2 varchar(20) = null,
#col3 varchar(20) = null,
...
#colN varchar(20) = null,
AS
DECLARE #count int
SET #count = 1
DECLARE #qry nvarchar(max)
SET #qry = 'SELECT #count = count(*) FROM aTable WHERE '
IF #col1 IS NOT EMPTY
BEGIN
SET #qry = #qry + ' col1 = '+ ''''+#col1+''''
END
IF #col2 IS NOT EMPTY
BEGIN
SET #qry = #qry + ' AND col2 = '+ ''''+#col2+''''
END
...
...
IF #colN IS NOT EMPTY
BEGIN
SET #qry = #qry + ' colN = '+ ''''+#colN+''''
END
/*once all the stored procedure variables are done processing, close the sql*/
SET #qry = #qry + ' )'
/*final #qry value would end up looking like as follows
SELECT #count = count(*)
FROM aTable
WHERE (col1 = 'val1' AND col2 = 'val2' AND col3 = 'val3')*/
Now, within this stored procedure I want to know the value of #count and if it is '0' then continue with the 'INSERT' procedure. I am stuck at getting '#count' value within this stored procedure.
Any thoughts?

Why do you want to use dynamic sql for this just Write a query with a WHERE Clause something like this
CREATE PROCEDURE usp_aTable_ADD --<-- Use usp
#col1 varchar(20) = null,
#col2 varchar(20) = null,
#col3 varchar(20) = null,
...
#colN varchar(20) = null,
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Count INT;
SELECT #count = count(*)
FROM aTable
WHERE
(#col1 IS NULL OR Col1 = #col1)
AND (#col2 IS NULL OR Col2 = #col2)
AND (#col3 IS NULL OR Col3 = #col3)
.
.
AND (#colN IS NULL OR ColN = #colN)
--And then at the end of your Proc just return the #Count Variable
RETURN #count;
END

Use an output parameter:
EXECUTE sp_executesql #qry, N'#Count INT OUTPUT', #Count OUTPUT;
For a simple test:
DECLARE #count INT;
DECLARE #qry NVARCHAR(MAX) = 'SELECT #Count = 10';
EXECUTE sp_executesql #qry, N'#Count INT OUTPUT', #Count OUTPUT;
SELECT #Count;
Finally, you never open the parentheses in your WHERE Clause:
SET #qry = 'SELECT #count = count(*) FROM aTable WHERE '
But you go on to close them:
SET #qry = #qry + ' )'
Also, if all your parameters are null even if you did open the parentheses you will get an incorrect syntax error since your SQL will be:
SELECT #count = count(*) FROM aTable WHERE ();
Or if #Col1 is NULL You will get:
SELECT #count = count(*) FROM aTable WHERE AND col2 = col2();
You can avoid this by firstly removing the parenthesis. WHERE A = 1 means exactly the same as WHERE (A = 1), and by adding a meaningless where first:
SET #qry = 'SELECT #count = count(*) FROM aTable 1 = 1 ';
IF #col1 IS NOT EMPTY
BEGIN
SET #qry = #qry + ' AND col1 = '+ ''''+#col1+''''
END
Finally I would recommend further parameterising your query.
SET #qry = 'SELECT #count = count(*) FROM aTable WHERE 1 = 1 '
DECLARE #ParamDef NVARCHAR(MAX) = '#Col1 VARCHAR(20), #Col2 VARCHAR(20), #Col3 VARCHAR(20), #Count INT OUTPUT';
IF #Col1 IS NOT NULL
SET #qry = #qry + ' AND Col1 = #Col1';
IF #Col2 IS NOT NULL
SET #qry = #qry + ' AND Col2 = #Col2';
IF #Col3 IS NOT NULL
SET #qry = #qry + ' AND Col3 = #Col3';
EXECUTE sp_executesql #qry, #ParamDef, #Col1, #Col2, #Col3, #Count OUTPUT;
Working Examples on SQL Fiddle

Related

How to get dynamic SQL query output into declared variable in SQL Server? [duplicate]

I have a piece of dynamic SQL I need to execute, I then need to store the result into a variable.
I know I can use sp_executesql but can't find clear examples around about how to do this.
If you have OUTPUT parameters you can do
DECLARE #retval int
DECLARE #sSQL nvarchar(500);
DECLARE #ParmDefinition nvarchar(500);
DECLARE #tablename nvarchar(50)
SELECT #tablename = N'products'
SELECT #sSQL = N'SELECT #retvalOUT = MAX(ID) FROM ' + #tablename;
SET #ParmDefinition = N'#retvalOUT int OUTPUT';
EXEC sp_executesql #sSQL, #ParmDefinition, #retvalOUT=#retval OUTPUT;
SELECT #retval;
But if you don't, and can not modify the SP:
-- Assuming that your SP return 1 value
create table #temptable (ID int null)
insert into #temptable exec mysp 'Value1', 'Value2'
select * from #temptable
Not pretty, but works.
DECLARE #vi INT
DECLARE #vQuery NVARCHAR(1000)
SET #vQuery = N'SELECT #vi= COUNT(*) FROM <TableName>'
EXEC SP_EXECUTESQL
#Query = #vQuery
, #Params = N'#vi INT OUTPUT'
, #vi = #vi OUTPUT
SELECT #vi
DECLARE #tab AS TABLE (col1 VARCHAR(10), col2 varchar(10))
INSERT into #tab EXECUTE sp_executesql N'
SELECT 1 AS col1, 2 AS col2
UNION ALL
SELECT 1 AS col1, 2 AS col2
UNION ALL
SELECT 1 AS col1, 2 AS col2'
SELECT * FROM #tab
Return values are generally not used to "return" a result but to return success (0) or an error number (1-65K). The above all seem to indicate that sp_executesql does not return a value, which is not correct. sp_executesql will return 0 for success and any other number for failure.
In the below, #i will return 2727
DECLARE #s NVARCHAR(500)
DECLARE #i INT;
SET #s = 'USE [Blah]; UPDATE STATISTICS [dbo].[TableName] [NonExistantStatisticsName];';
EXEC #i = sys.sp_executesql #s
SELECT #i AS 'Blah'
SSMS will show this
Msg 2727, Level 11, State 1, Line 1
Cannot find index 'NonExistantStaticsName'.
If you want to return more than 1 value use this:
DECLARE #sqlstatement2 NVARCHAR(MAX);
DECLARE #retText NVARCHAR(MAX);
DECLARE #ParmDefinition NVARCHAR(MAX);
DECLARE #retIndex INT = 0;
SELECT #sqlstatement = 'SELECT #retIndexOUT=column1 #retTextOUT=column2 FROM XXX WHERE bla bla';
SET #ParmDefinition = N'#retIndexOUT INT OUTPUT, #retTextOUT NVARCHAR(MAX) OUTPUT';
exec sp_executesql #sqlstatement, #ParmDefinition, #retIndexOUT=#retIndex OUTPUT, #retTextOUT=#retText OUTPUT;
returned values are in #retIndex and #retText
Declare #variable int
Exec #variable = proc_name
DECLARE #ValueTable TABLE
(
Value VARCHAR (100)
)
SELECT #sql = N'SELECT SRS_SizeSetDetails.'+#COLUMN_NAME+' FROM SRS_SizeSetDetails WHERE FSizeID = '''+#FSizeID+''' AND SRS_SizeSetID = '''+#SRS_SizeSetID+'''';
INSERT INTO #ValueTable
EXEC sp_executesql #sql;
SET #Value='';
SET #Value = (SELECT TOP 1 Value FROM #ValueTable)
DELETE FROM #ValueTable
This worked for me:
DECLARE #SQL NVARCHAR(4000)
DECLARE #tbl Table (
Id int,
Account varchar(50),
Amount int
)
-- Lots of code to Create my dynamic sql statement
insert into #tbl EXEC sp_executesql #SQL
select * from #tbl
Here's something you can try
DECLARE #SqlStatement NVARCHAR(MAX) = ''
,#result XML
,#DatabaseName VARCHAR(100)
,#SchemaName VARCHAR(10)
,#ObjectName VARCHAR(200);
SELECT #DatabaseName = 'some database'
,#SchemaName = 'some schema'
,#ObjectName = 'some object (Table/View)'
SET #SqlStatement = '
SELECT #result = CONVERT(XML,
STUFF( ( SELECT *
FROM
(
SELECT TOP(100)
*
FROM ' + QUOTENAME(#DatabaseName) +'.'+ QUOTENAME(#SchemaName) +'.' + QUOTENAME(#ObjectName) + '
) AS A1
FOR XML PATH(''row''), ELEMENTS, ROOT(''recordset'')
), 1, 0, '''')
)
';
EXEC sp_executesql #SqlStatement,N'#result XML OUTPUT', #result = #result OUTPUT;
SELECT DISTINCT
QUOTENAME(r.value('fn:local-name(.)', 'VARCHAR(200)')) AS ColumnName
FROM #result.nodes('//recordset/*/*') AS records(r)
ORDER BY ColumnName
This was a long time ago, so not sure if this is still needed, but you could use ##ROWCOUNT variable to see how many rows were affected with the previous sql statement.
This is helpful when for example you construct a dynamic Update statement and run it with exec. ##ROWCOUNT would show how many rows were updated.
Here is the definition

Stored Procedure giving error

I'm trying to use a stored procedure to display the results of a table. The stored procedure is giving the error 'Procedure expects parameter '#parameters' of type 'ntext/nchar/nvarchar'
ALTER PROCEDURE COMNODE_PROC_SearchProduct --'','GUN',''
#PRODUCTID INT = NULL,
#PRODUCT_NAME VARCHAR(500) = NULL,
#PRODUCT_POINTS INT = NULL
AS
BEGIN
SET NOCOUNT ON;
Declare #SQLQuery AS NVarchar(MAX)
Declare #ParamDefinition AS NVarchar(MAX)
Set #ParamDefinition = '#ID INT,
#NAME VARCHAR(500),
#POINTS INT'
Set #SQLQuery = 'SELECT PRODUCT_ID,PRODUCT_NAME,PRODUCT_REDEEM_POINTS FROM TBL_REDEEM_PRODUCT WHERE (1 = 1)';
If #PRODUCTID Is Not Null
Set #SQLQuery = #SQLQuery + ' And (PRODUCT_ID ='+CAST(#PRODUCTID AS VARCHAR(500) )
If #PRODUCT_NAME Is Not Null
Set #SQLQuery = #SQLQuery + ' And (PRODUCT_NAME =' + CAST(#PRODUCT_NAME AS VARCHAR(500) )
If #PRODUCT_POINTS Is Not Null
Set #SQLQuery = #SQLQuery + ' And (PRODUCT_REDEEM_POINTS ='+ CAST(#PRODUCT_POINTS AS VARCHAR(500))
Execute sp_Executesql #SQLQuery,
#ID = #PRODUCTID ,
#NAME = #PRODUCT_NAME ,
#POINTS = #PRODUCT_POINTS;
END
You need to pass in the parameter definition to sp_executesql, see: https://msdn.microsoft.com/en-us/library/ms188001.aspx
Execute sp_Executesql #SQLQuery,
#ParamDefinition,
#ID = #PRODUCTID ,
#NAME = #PRODUCT_NAME ,
#POINTS = #PRODUCT_POINTS;
One of the main reasons you would want to use sp_executesql so do not have to concatenate variables, have you can be protected from sql-injection attack using the parameterised query.
You concatenating parameters just kill the purpose and makes your query vulnerable to sql-injection . See below the proper use of dynamic sql the safe way.
ALTER PROCEDURE COMNODE_PROC_SearchProduct --'','GUN',''
#PRODUCTID INT = NULL,
#PRODUCT_NAME VARCHAR(500) = NULL,
#PRODUCT_POINTS INT = NULL
AS
BEGIN
SET NOCOUNT ON;
Declare #SQLQuery AS NVarchar(MAX);
Declare #ParamDefinition AS NVarchar(MAX);
Set #ParamDefinition = N'#ID INT, #NAME VARCHAR(500), #POINTS INT';
-- A much cleaner way to write this would be...
Set #SQLQuery = N'SELECT PRODUCT_ID,PRODUCT_NAME,PRODUCT_REDEEM_POINTS
FROM TBL_REDEEM_PRODUCT
WHERE (1 = 1)'
+ CASE WHEN #PRODUCTID Is Not Null
THEN N' And PRODUCT_ID = #ID ' ELSE N' ' END
+ CASE WHEN #PRODUCT_NAME Is Not Null
THEN N' And PRODUCT_NAME = #NAME ' ELSE N' ' END
+ CASE WHEN #PRODUCT_POINTS Is Not Null
THEN N' And PRODUCT_REDEEM_POINTS = #POINTS' ELSE N' ' END
Execute sp_Executesql #SQLQuery
,#ParamDefinition --<-- this was missing
,#ID = #PRODUCTID
,#NAME = #PRODUCT_NAME
,#POINTS = #PRODUCT_POINTS;
END

Can I use variable in condition statement as value with where condition that test that value

I have the following stored procedure:
ALTER proc [dbo].[insertperoll] #name nvarchar(50) , #snum int , #gnum int
as
DECLARE #value nvarchar(10)
SET #value = 's'+CONVERT(nvarchar(50),#snum)
DECLARE #sqlText nvarchar(1000);
DECLARE #sqlText2 nvarchar(1000);
DECLARE #sqlText3 nvarchar(1000);
declare #g nvarchar(50) = '''g1'''
SET #sqlText = N'SELECT ' + #value + N' FROM dbo.GrideBtable'
SET #sqlText2 = ' where Gnumber = '+#g --here is the problem it error invalid column name -- the #g is value from the table condition
set #sqlText3 = #sqlText+#sqlText2
Exec (#sqlText3) -- here how can i save the result of the exec into varibale
declare #sal nvarchar(50) = #sqlText3
insert employ (name,Snumber,Gnumber,Salary) values(#name,#snum,#gnum,#sal)
QUESTION: How to put in condition variable gets value from the table when i exec it it think that the #g is column but its not its a value from the table to test it so i display one value after the exec the other QUESTION is how to save the result from the exec in variable and then use that value
I'm using SQL Server 2008 (9.0 RTM)
This will be a stored procedure
Thanks in advance
Not sure why you would go through all the loops to insert into the table where you can have a simple insert query like ..
ALTER PROC dbo.[insertperoll] #name nvarchar(50) , #snum int , #gnum int
AS
insert employ (name, Snumber, Gnumber, Salary)
select #name
, #sum
, #gnum
, case when #snum = 1 then s1
when #snum = 2 then s2
when #snum = 3 then s3
when #snum = 4 then s4
end as Salary
from dbo.GrideBtable
where Gnumber = #gnum
If your intent is to have the proc retrieve a salary value from a column determined from the parameter snum and then make an insert into employ using the values passed as parameters and the salary retrieved I think you could refactor your procedure to this:
CREATE proc [dbo].[insertperoll] #name nvarchar(50) , #snum int , #gnum int AS
DECLARE #g NVARCHAR(50) = 'g1'
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'INSERT employ (name,Snumber,Gnumber,Salary) '
SET #sql += N'SELECT ' + QUOTENAME(#name, '''')
SET #sql += N', ' + CAST(#snum AS NVARCHAR(50))
SET #sql += N', ' + CAST(#gnum AS NVARCHAR(50))
SET #sql += N', s' + CAST(#snum AS NVARCHAR(50))
SET #sql += N' FROM dbo.GrideBtable'
SET #sql += N' WHERE Gnumber = ' + QUOTENAME(#g, '''')
EXEC (#sql)
Of course you could add the #g variable to the procedure parameters instead of having it hard coded in the procedure and call it as:
EXEC insertperoll #name='john', #snum=10, #gnum=100, #g='g1'
Sample SQL Fiddle (with some assumptions made about table structure)
You could do this using sp_executesql instead of exec() since this will allow you to use parameters, you can use an output parameter to get the value from the query:
DECLARE #SQL NVARCHAR(MAX) = N'SELECT #val = ' + CONVERT(NVARCHAR(10),#snum) +
N' FROM dbo.GrideBtable WHERE Gnumber = #G1';
DECLARE #val INT; -- NOT SURE OF DATATYPE REQUIRED
EXECUTE sp_executesql #SQL, N'#G1 VARCHAR(20), #val INT OUT', 'G1', #val OUT;

Converting dynamic sql query's COUNT result to INT

I am trying to search within the values (table names) returned from a query to check if there is a record and some values in that record are null. If they are, then I want to insert the table's name to a temporary table. I get an error:
Conversion failed when converting the varchar value 'count(*)
FROM step_inusd_20130618 WHERE jobDateClosed IS NULL' to data type int.
This is the query:
DECLARE #table_name VARCHAR(150)
DECLARE #sql VARCHAR(1000)
DECLARE #test int
SELECT #table_name = tableName FROM #temp WHERE id = #count
SET #sql = 'SELECT * FROM ' + #table_name + ' WHERE jobDateClosed IS NULL'
--ERROR is below:
select #test = 'count(*) FROM ' + #table_name + ' WHERE jobDateClosed IS NULL'
--PRINT #sql
-- EXEC(#sql)
IF #test > 0
BEGIN
INSERT INTO #temp2 (tablename) VALUES ( #table_name);
END
SET #count = #count + 1
Any ideas how to convert the result of the count into an integer?
Check for sp_executesql where you may define output parameters.
DECLARE #table_name VARCHAR(150)
DECLARE #sql VARCHAR(1000)
DECLARE #test int
SELECT #table_name = tableName FROM #temp WHERE id = #count
DECLARE #SQLString nvarchar(500);
DECLARE #ParmDefinition nvarchar(500);
SET #SQLString = N'SELECT #test = count(*) FROM ' + #table_name + ' WHERE jobDateClosed IS NULL'
SET #ParmDefinition = N'#test int OUTPUT';
EXECUTE sp_executesql #SQLString, #ParmDefinition, #test=#test OUTPUT;
IF #test > 0
BEGIN
INSERT INTO #temp2 (tablename) VALUES ( #table_name);
END
SET #count = #count + 1
Shouldn't be "SET" instead of "select" ?
E.g., changing:
select #test = 'count(*) FROM ' + #table_name + ' WHERE jobDateClosed IS NULL'
for:
SET #test = 'select count(*) FROM ' + #table_name + ' WHERE jobDateClosed IS NULL'
As I see, your problem is, that $test variable is INT and you are trying to assign to it the TEXT value 'count ...'
Use aproach like:
SELECT somevalue INTO myvar FROM mytable WHERE uid=1;
I trimmed out the stuff not needed to show how to do this, so here it is:
DECLARE #table_name VARCHAR(150)
DECLARE #CountStatement NVARCHAR(1000)
DECLARE #test int
SELECT #table_name = tableName FROM #temp WHERE id = #count
SET #CountStatement = 'select #test = count(*) FROM ' + #table_name + ' WHERE jobDateClosed IS NULL'
EXECUTE sp_executesql #CountStatement, N'#test INT OUTPUT', #test OUTPUT;
SELECT #test

Why does EXEC retport an error of MUST DECLARE SCALAR VARIABLE

I've been struggling with the script bellow and I can't figure out a better way to do it.
Does anyone see the problem? I'm declaring the variable correctly. Whys is it failing? Thanks for your help!
DECLARE #var1 as VarChar(50)
DECLARE #var2 as VarChar(50)
SET #Var1 = '1, 2, 3, 4, 5'
EXEC('IF (select count(*) from Table1 where Column1 in (' + #Var1 + ')) = 5
SET #Var2 = ''True''
ELSE
SET #Var2 = ''False''')
SELECT #Var2
Error Message:
MUST DECLARE SCALAR VARIABLE #Var2
You need to use sp_executesql if you want to get values from parameters.
To shorten the query, use CASE.
DECLARE #var1 as VarChar(50)
DECLARE #var2 as VarChar(50)
SET #Var1 = '1, 2, 3, 4, 5'
SET #sqlCommand = 'SELECT #Var = CASE WHEN count(*) = 5 THEN ''TRUE'' ELSE ''FALSE'' END FROM Table1 where Column1 IN (' + #Var1 + ')'
EXECUTE sp_executesql #sqlCommand, N'#Var VARCHAR(5) OUTPUT', #Var=#var2 OUTPUT
SELECT #Var2
if you want to know if all ID from #Var1 is present on the table, you also need to use DISTINCT
SET #sqlCommand = 'SELECT #Var = CASE WHEN count(DISTINCT Column1) = 5 THEN ''TRUE'' ELSE ''FALSE'' END FROM Table1 where Column1 IN (' + #Var1 + ')'
sp_executesql MSDN Docs
Since exec is executing dynamic part of SQL which is inside single quote. But #var2 is declared outside exec. To solve this declare and select #var2 inside exec or use #var2 as set '+#var2+'
Use sp_executeSQL instead of exec and use an output parameter
E.g. How to get sp_executesql result into a variable?
For example
DECLARE #var1 as VarChar(50)
DECLARE #var2 as VarChar(50)
DECLARE #ParmDefinition nvarchar(500);
SET #Var1 = '1, 2, 3, 4, 5'
SET #ParmDefinition = N'#Var2 VarChar(50) OUTPUT';
DECLARE #sSQL nvarchar(500);
set #sSQL = 'IF (select count(*) from Table1 where Column1 in (' + #Var1 + ')) = 5
SET #Var2 = ''True''
ELSE
SET #Var2 = ''False'''
EXEC sp_executesql #sSQL, #ParmDefinition, #Var2=#Var2 OUTPUT;
SELECT #Var2;
There are two things there.. first where are the ";" ending the sentences. Also you need to scape the varchar values.
Agree with the other answer... use sp_executeSQL to run the dynamic sql.
An example of declare....
USE AdventureWorks2012;
GO
DECLARE #find varchar(30);
/* Also allowed:
DECLARE #find varchar(30) = 'Man%';
*/
SET #find = 'Man%';
SELECT p.LastName, p.FirstName, ph.PhoneNumber
FROM Person.Person AS p
JOIN Person.PersonPhone AS ph ON p.BusinessEntityID = ph.BusinessEntityID
WHERE LastName LIKE #find;
An example of sp_executeSQL..
DECLARE #IntVariable int;
DECLARE #SQLString nvarchar(500);
DECLARE #ParmDefinition nvarchar(500);
/* Build the SQL string one time.*/
SET #SQLString =
N'SELECT EmployeeID, NationalIDNumber, Title, ManagerID
FROM AdventureWorks.HumanResources.Employee
WHERE ManagerID = #ManagerID';
SET #ParmDefinition = N'#ManagerID tinyint';
/* Execute the string with the first parameter value. */
SET #IntVariable = 197;
EXECUTE sp_executesql #SQLString, #ParmDefinition,
#ManagerID = #IntVariable;
/* Execute the same string with the second parameter value. */
SET #IntVariable = 109;
EXECUTE sp_executesql #SQLString, #ParmDefinition,
#ManagerID = #IntVariable;
There are many reasons to stay away from executing sql strings. I would rewrite this to be:
declare #cnt int
select #cnt = count(1)
from Table1
where Column1 in
(select items
from dbo.Split(#Var1,',')
)
return #cnt -- then the calling function says if this is true or false.
for the Split function see: HERE