I have a stored procedure that outputs a select statement as you see below:
select case when count(*) > 10 then ''A1'' else ''B1'' end as id, name, address from database.dbo.student
Now I want to write a stored procedure that takes such a select statement as string input, and return all the above outputs. I'm not sure where to assign the output variables.
ALTER PROCEDURE dbo.selectAttributes
#select_statement Nvarchar(MAX),
#id Nvarchar(255) OUT,
#name nvarchar(255) OUT,
#address nvarchar(255) OUT
AS
BEGIN
DECLARE #query nvarchar(MAX)
SET #query = #select_statement
EXEC sp_executesql #query,
N'#select_statement Nvarchar(MAX),
#idOUT Nvarchar(255) OUTPUT,
#nameOUT nvarchar(255) OUTPUT,
#addressOUT nvarchar(255) OUTPUT',
#select_statement, #id OUTPUT, #name OUTPUT, #address OUTPUT
END
I think you are close:
ALTER PROCEDURE dbo.selectAttributes (
#select_statement Nvarchar(MAX),
#id Nvarchar(255) OUTPUT,
#name nvarchar(255) OUTPUT,
#address nvarchar(255) OUTPUT
) AS
BEGIN
EXEC sp_executesql #select_statement,
N'#id Nvarchar(255) OUTPUT, #name nvarchar(255) OUTPUT, #address nvarchar(255) OUTPUT',
#id = #id OUTPUT,
#name = #name OUTPUT,
#address = #address OUTPUT
END;
In other words:
You have to put the OUTPUT keyword everywhere.
The query string does not go in as a parameter.
I don't rename the parameters, but if you do, they have to be declared somewhere.
Related
Given a simple stored procedure, that populates a OUTPUT parameter and also RETURNs a value, such as:
CREATE PROCEDURE sp_test
(
#param_out INT OUTPUT
)
AS BEGIN
SELECT #param_out = 9
RETURN 2
END
How can I call this procedure using sp_executesql and capture both of these values?
I've tried:
DECLARE #ret INT, #param_out INT
EXEC SP_EXECUTESQL N'EXEC #ret = sp_test',
N'#ret INT OUTPUT, #param_out INT OUTPUT',
#ret OUTPUT,
#param_out OUTPUT
SELECT #ret, #param_out
However this complains that #param_out was not supplied:
Procedure or function 'sp_test' expects parameter '#param_out', which was not supplied.
You would need to pass both values as OUTPUT parameters to sp_executesql as well:
DECLARE #ret int,
#param_out int;
EXEC sp_executesql N'EXEC #ret = sp_test #param_out OUT;',
N'#ret INT OUTPUT, #param_out INT OUTPUT',
#ret OUTPUT,
#param_out OUTPUT;
SELECT #ret,
#param_out;
I must ask though, why do you want to do this? There's nothing dynamic about this SQL, so why are you using sp_executesql? I also advise against using the return value of an SP; you should really be using another OUTPUT parameter.
You don't need sp_executesql here just because the stored procedure name is dynamic.
The EXEC grammar accepts #module_name_var.
You can do
DECLARE #ret INT,
#param_out INT
DECLARE #procname SYSNAME = 'sp_test'
EXEC #ret = #procname
#param_out OUTPUT
and the procedure with the name in #procname will be executed
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.
I have a query which returns a single result.
#query='select name from studtable where id=1'
How should I write query so that result is saved in string and #result contains result.
#result=exec(#query)
To execute a string, we recommend that you use the sp_executesql stored procedure
instead of the EXECUTE statement. Because this stored procedure supports parameter
substitution, sp_executesql is more versatile than EXECUTE; and because
sp_executesql generates execution plans that are more likely to be reused by SQL
Server, sp_executesql is more efficient than EXECUTE.
Read more here:http://technet.microsoft.com/en-us/library/ms175170(v=sql.105).aspx
So you can write as"
DECLARE #SQLString NVARCHAR(500)
DECLARE #ParmDefinition NVARCHAR(500)
DECLARE #IntVariable INT
DECLARE #name varchar(30)
SET #SQLString = N'SELECT #nameOUT = name
from studtable where id=#id'
SET #ParmDefinition = N'#id tinyint,
#nameOUT varchar(30) OUTPUT'
SET #IntVariable = 1
EXECUTE sp_executesql
#SQLString,
#ParmDefinition,
#id = #IntVariable,
#nameOUT=#name OUTPUT
SELECT #name
You can do something like below to store the result using sp_executesql with output parameter. Observed from here Assign result of dynamic sql to variable
declare #ret int
set #ret = 0
set #query='select name from studtable where id=1'
exec sp_executesql #query, N'#var int out', #ret out
select #ret
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
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'