Output parameter in stored procedure is null - sql

I can't seem to get the output parameter right, it's empty where I'm exepecting 'TEMPTABLE'?
create table TEMPTABLE
(
Id int identity primary key,
Name nvarchar(50) not null,
);
GO
insert into TEMPTABLE(Name) values('Name 1');
GO
DECLARE #PARMOUTP nvarchar(50)
DECLARE #ParameterDefinition AS NVARCHAR(2000)
DECLARE #DestTable VARCHAR(50)
declare #sql nvarchar(max)
set #sql = 'SELECT OBJECT_NAME(OBJECT_ID) AS TABLENAME
FROM SYS.IDENTITY_COLUMNS
WHERE OBJECT_NAME(OBJECT_ID) = #DestTable'
set #DestTable = 'TEMPTABLE'
set #ParameterDefinition = '#DestTable VARCHAR(50), #PARMOUTP VARCHAR(50) OUTPUT'
EXECUTE sp_executesql #sql,
#ParameterDefinition,
#DestTable,
#PARMOUTP OUTPUT
print #PARMOUTP;
print isnull(#PARMOUTP, 'PARMOUTP is null!');
What am I doing wrong here?
Regards,
Mike

Your query is not suited for sp_executesql with one parameter.
In the query you should assign value to that parameter and execute it.
Something like this:
create table TEMPTABLE
(
Id int identity primary key,
Name nvarchar(50) not null,
);
GO
insert into TEMPTABLE(Name) values('Name 1');
GO
DECLARE #PARMOUTP nvarchar(50)
DECLARE #ParameterDefinition AS NVARCHAR(2000)
DECLARE #DestTable VARCHAR(50)
declare #sql nvarchar(max)
set #sql = 'SELECT #PARMOUTP = OBJECT_NAME(OBJECT_ID) AS TABLENAME
FROM SYS.IDENTITY_COLUMNS
WHERE OBJECT_NAME(OBJECT_ID) = #DestTable'
set #DestTable = 'TEMPTABLE'
set #ParameterDefinition = '#DestTable VARCHAR(50), #PARMOUTP VARCHAR(50) OUTPUT'
EXECUTE sp_executesql #sql,
#ParameterDefinition,
#DestTable,
#PARMOUTP OUTPUT
print #PARMOUTP;
print isnull(#PARMOUTP, 'PARMOUTP is null!');
Remember that you are returning scalar value from your dynamic query.

In your dynamic SQL statement, you're never assigning any value to the output parameter!
You're selecting the table name as a result set - but it never gets stored into the output variable.
Try this:
set #sql = 'SELECT #PARMOUTP = OBJECT_NAME(OBJECT_ID)
FROM SYS.IDENTITY_COLUMNS
WHERE OBJECT_NAME(OBJECT_ID) = #DestTable'

Related

T sql - How to store results from a dynamic query using EXEC or EXECUTE sp_executesql

I am trying to write a dynamic query. Let's say i have a table like below, which represents the hierarchy level of a sales agent:
AgentNumber Level1Agent Level2Agent Level3Agent Level4Agent Level5Agent
1122334455 1122334499 1122334488 1122334477 1122334466 1122334455
I want to be able to dynamically select a level based on a specified agent. My EXECUTE statement seems to work correctly, but how do I get the result stored in a variable I can use later? Every answer I have found seems to only get me a success return variable, not the actual query result.
Below is my code:
DECLARE #level INT = 1;
DECLARE #agent CHAR(10) = 1122334455;
DECLARE #colname NVARCHAR(11) = CONCAT('Level',#level,'Agent');
DECLARE #whereclause NVARCHAR(35) = CONCAT('WHERE AgentNumber = ',#agent);
DECLARE #qry NVARCHAR(300) = 'SELECT ' + #colname + ' FROM dbo.TABLE ' + #whereclause;
DECLARE #up NVARCHAR(10);
EXECUTE sp_executesql #qry, #up OUT
SELECT #up
The output of #up is NULL. If I change the last two lines to:
EXECUTE #up = sp_executesql #qry
SELECT #up
Now the output of #up is 0.
I want the output of 1122334499 and I need it stored in a variable that can later be used and inserted into a table.
Here is a fully functional example of how you can do this. Notice this is using a parameterized where clause and quotename around the column name in the dynamic sql to prevent sql injection.
if OBJECT_ID('tempdb..#Agents') is not null
drop table #Agents
create table #Agents
(
AgentNumber char(10)
, Level1Agent char(10)
, Level2Agent char(10)
, Level3Agent char(10)
, Level4Agent char(10)
, Level5Agent char(10)
)
insert #Agents
select '1122334455', '1122334499', '1122334488', '1122334477', '1122334466', '1122334455'
DECLARE #level INT = 3;
DECLARE #agent CHAR(10) = 1122334455;
DECLARE #colname NVARCHAR(11) = CONCAT('Level',#level,'Agent');
declare #agentout char(10)
DECLARE #qry NVARCHAR(300) = 'SELECT #agent_out = ' + quotename(#colname) + ' FROM #Agents WHERE AgentNumber = #agentin';
EXECUTE sp_executesql #qry, N'#agentin char(10), #agent_out char(10) output', #agentin = #agent, #agent_out = #agentout output
select #agentout
You can try this :
DECLARE #level INT = 1;
DECLARE #agent CHAR(10) = 1122334455;
DECLARE #colname NVARCHAR(11) = CONCAT('Level',#level,'Agent');
DECLARE #whereclause NVARCHAR(35) = CONCAT('WHERE AgentNumber = ',#agent);
DECLARE #qry NVARCHAR(300) = 'SELECT #agentout=' + #colname + ' FROM dbo.TABLE ' + #whereclause;
DECLARE #up NVARCHAR(10);
EXECUTE sp_executesql #qry, N'#agentout NVARCHAR(10) OUTPUT', #agentout=#up OUTPUT
SELECT #up
Create a variable table and makes your query insert the results you want there. Something like this:
declare #results table(field1 varchar(max), field2 varchar(max));
declare #sqlStatement varchar(max);
set #sqlStatement = 'insert into #results(field1, field2) select field1, field2 from table';
EXECUTE #sqlStatement;
select * from #results; --It will print the results from your sql statement!

Dynamic Name for Database Table - Stored Procedure

I want to create dynamic name for my database tables. I declare variable and use it as my table name.
ERROR: Incorrect syntax near '#sample'. Expecting '.',ID or QUOTED_ID
CREATE PROCEDURE [dbo].[sp_SAMPLE]
#name nvarchar(50)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sample nvarchar(50);
SET #sample = 'tbl_function_' + #name;
Create Table #sample
(
id int not null,
UserType nvarchar(50) null,
paramater2 nvarchar(50) null
)
END
Is there any way to make my table name dynamic? Thank you.
I'm not sure why you would want to do this. But, you can do it using dynamic SQL:
CREATE PROCEDURE [dbo].[sp_SAMPLE]
#name nvarchar(50)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql nvarchar(max);
SET #sql = '
Create Table tbl_function_#name
(
id int not null,
UserType nvarchar(50) null,
paramater2 nvarchar(50) null
)';
SET #sql = REPLACE(#sql, '#name', #name);
EXEC sp_executesql #sql;
END;
Why would you want to create a separate table for each #name, instead of just inserting rows with #name in a column?
CREATE PROCEDURE [dbo].[sp_SAMPLE]
#name nvarchar(50)
AS
BEGIN
SET NOCOUNT ON
DECLARE #sql varchar(4000);
SET #sql = '
Create Table tbl_function_'+#name+'
(
id int not null,
UserType nvarchar(50) null,
paramater2 nvarchar(50) null
)'
EXEC (#sql)
END

Why does storing a value from sp_executesql return NULL?

Once more I stumble upon an SQL mishappening. The following code:
create trigger tc_trigger_olt_UPDATE on tick_orderline_type
for update as
declare #UserId varchar(32) --
declare #ClientId varchar(32) --
declare #CaseId varchar(32) --
declare #TableName varchar(64) --Used
declare #RecordId varchar(512) --Used
declare #Descr varchar(128) --
declare #RemoteIP varchar(16) --Used
select #TableName = object_name(parent_id) from sys.triggers where object_id = ##procid
select #RemoteIP = client_net_address from sys.dm_exec_connections where Session_id = ##SPID
if exists(select * from information_schema.columns where table_name = #TableName and column_name = 'id')
begin
select #RecordId = id from inserted
end
declare #sql nvarchar(max)
set #sql = 'select case_id from '+#TableName+' where id = '''+#RecordId+'''';
exec sp_executesql #sql, N'#out_param varchar(32) OUTPUT', #out_param=#CaseId OUTPUT
execute update_tick_orderline_type #UserId, #ClientId, #CaseId, #TableName, #RecordId, #Descr, #RemoteIP
Is used for filling and audit-table in SSMS, which works. It stores which table, which record, on which date and from which IP-Address the table has been edited.
I am currently trying to get which client's data, for which case, has been edited using the following snipped from above:
declare #sql nvarchar(max)
set #sql = 'select case_id from '+#TableName+' where id = '''+#RecordId+'''';
exec sp_executesql #sql, N'#out_param varchar(32) OUTPUT', #out_param=#CaseId OUTPUT
In my Results window (the one that shows select statement results), I can see that the #sql statement has selected the correct case_id; storing it in the #CaseId variable however, returns NULL. Once I have the #CaseId I can get the #ClientId, so I'm stumped here.
Why is the statement outputting the correct case_id, but stores it as NULL?
A little sidenote: the case_id is only being output when the exec statement is present, otherwise it is not
I managed to fix my own predicament, by changing the previous 'select x from #TableName' block, to this:
declare #sql nvarchar(max)
declare #params nvarchar(max)
set #params = N'#iCaseId varchar(max) output'
select #sql = N'select #iCaseId = case_id from '+quotename(#TableName)+
' where id = '''+#RecordId+''''
execute sp_executesql #sql, #params, #CaseId output
What changed?
I declared an additional #params variable, for readability
Instead of set I select-ed the #sql variable
quotename now surrounds the #TableName variable
instead of the last param for sp_executesql being #x = #y output I simply replaced it with #y output
Conclusion
After several tests (mainly removing some of the changes), I can conclude that the last point on that list was key to solving the issue.
With quotename removed from the statement, I still manage to save the result in a variable, and since the #param variable is not mandatory, but rather a personal preference, it also had no real significance.

TSQL - Parameters and dynamic table names

Please see the DDL below:
CREATE TABLE Person (ID int identity, Name varchar(30), primary key(ID))
INSERT INTO Person (Name) VALUES ('Ian')
Please see the TSQL below:
Declare #ID int
Declare #Name varchar(30)
set #Name = 'Ian'
select #ID=ID FROM Person WHERE Name=#Name
Print #ID
This works as I would expect i.e. 1 is printed to the screen. I want to make a change so that the table name is dynamic i.e.
Declare #ID int
Declare #Name varchar(30)
Declare #TableName as varchar(30)
set #TableName= 'Person'
set #Name = 'Ian'
select #ID=ID FROM #TableName WHERE Name=#Name
Print #ID
The above SQL obviously does not work. I realise that I have to use TSQL. I have tried using EXEC and sp_executesql, but I can only get part way there. How do I implement this. The SQL statement must be: select #ID=ID FROM #TableName WHERE Name=#Name.
You will need to use dynamic sql for this. Have a look here...
Declare #ID int
Declare #Name varchar(30)
Declare #TableName sysname
DECLARE #Sql NVARCHAR(MAX);
set #TableName= 'Person'
set #Name = 'Ian'
SET #Sql = N'select #ID = ID FROM ' + QUOTENAME(#TableName)
+ N' WHERE Name = #Name'
EXECUTE sp_executesql #Sql
,N'#ID int OUTPUT, #Name varchar(30)'
,#ID OUTPUT
,#Name
PRINT #ID

Pass a TABLE variable to sp_executesql

I'm trying to pass a TABLE variable to the sp_executesql procedure:
DECLARE #params NVARCHAR(MAX)
SET #params = '#workingData TABLE ( col1 VARCHAR(20),
col2 VARCHAR(50) )'
EXEC sp_executesql #sql, #params, #workingData
I get the error:
Msg 156, Level 15, State 1, Line 1
Incorrect syntax near the keyword 'TABLE'.
I tried omitting the column specification after 'TABLE'. I also tried to declare the table as a variable inside the dynamic SQL. But no luck...
Seems to me that TABLE variables aren't allowed to be passed as parameters in this procedure?. BTW: I'm running MSSQL2008 R2.
I'm not interested in using a local temp table like #workingData because I load the working data from another procedure:
INSERT INTO #workingData
EXEC myProc #param1, #param2
Which I cannot do directly into a temp varaible (right?)...
Any help appreciated!
If you are using SQL Server 2008, to pass a table variable to a stored procedure you must first define the table type, e.g.:
CREATE TYPE SalesHistoryTableType AS TABLE
(
[Product] [varchar](10) NULL,
[SaleDate] [datetime] NULL,
[SalePrice] [money] NULL
)
GO
or use an existing table type stored in the database.
Use this query to locate existing table types
SELECT * FROM sys.table_types
To use in an stored procedure, declare an input variable to be the table:
CREATE PROCEDURE usp_myproc
(
#TableVariable SalesHistoryTableType READONLY
)
AS BEGIN
--Do stuff
END
GO
Populate the table variable before passing to the stored procedure:
DECLARE #DataTable AS SalesHistoryTableType
INSERT INTO #DataTable
SELECT * FROM (Some data)
Call the stored procedure:
EXECUTE usp_myproc
#TableVariable = #DataTable
Further discussions here.
OK, this will get me what I want, but surely isn't pretty:
DECLARE #workingData TABLE ( col1 VARCHAR(20),
col2 VARCHAR(20) )
INSERT INTO #workingData
EXEC myProc
/* Unfortunately table variables are outside scope
for the dynamic SQL later run. We copy the
table to a temp table.
The table variable is needed to extract data directly
from the strored procedure call above...
*/
SELECT *
INTO #workingData
FROM #workingData
DECLARE #sql NVARCHAR(MAX)
SET #sql = 'SELECT * FROM #workingData'
EXEC sp_executesql #sql
There must be a better way to pass this temporary resultset into sp_executesql!?
Regards
Alex
While this may not directly answer your question, it should solve your issue overall.
You can indeed capture the results of a Stored Procedure execution into a temporary table:
INSERT INTO #workingData
EXEC myProc
So change your code to look like the following:
CREATE TABLE #workingData ( col1 VARCHAR(20),
col2 VARCHAR(20) )
INSERT INTO #workingData
EXEC myProc
DECLARE #sql NVARCHAR(MAX)
SET #sql = 'SELECT * FROM #workingData'
EXEC sp_executesql #sql
Regards,
Tim
Alter PROCEDURE sp_table_getcount
#tblname nvarchar(50) ,
#totalrow int output
AS
BEGIN
Declare #params nvarchar(1000)
Declare #sql nvarchar(1000)
set #sql = N'Select #cnt= count(*) From #tbl'
set #params = N'#tbl nvarchar(50) , #cnt int OUTPUT'
Exec sp_executesql #sql , #params ,#tbl=#tblname , #cnt = #totalrow OUTPUT
END
GO
Please note that the above code will not work as table as a object is out of the scope.It will give you the error: must declare table variable.In order to work around we can do the following.
Alter PROCEDURE sp_table_getcount
#tblname nvarchar(50) ,
#totalrow int output
AS
BEGIN
Declare #params nvarchar(1000)
Declare #sql nvarchar(1000)
set #sql = N'Select #cnt= count(*) From dbo.' + #tblname
set #params = N'#cnt int OUTPUT'
Exec sp_executesql #sql , #params , #cnt = #totalrow OUTPUT
END
GO
So-called TableType is tricky. #Alex version should work. However, to simplify and faster performance, go check sys.tables for matching table name while not compromise security and performance.
Here it is
create proc [dbo].Test11
#t1 AS nvarchar(250), #t2 nvarchar(250)
AS
BEGIN
SET nocount ON;
DECLARE #query AS nvarchar(MAX)
if exists (select * from sys.tables where name = #t1) and
exists (select * from sys.tables where name = #t2)
begin
SET #query = N'select * FROM '+ #t1 + N' join ' + #t2 + N' ON ...' ;
select 'Safe and fast'
print #query
exec sp_executesql #query
end
else
select 'Bad, no way Jose.'
SET nocount OFF;
END
GO