I was writing some dynamic SQL today and I needed to output a parameter for the count in the dynamic SQL. I’ve always use EXECUTE but I did not know how to use the value returned from the executed SQL. Turns out that sp_executesql (which I was always aware of but never used) supports the setting of parameter returns, which I will get onto later. Sp_executesql is also a far more efficient method of executing dynamic SQL. This is because of something called parameter substitution. Being able to substitute parameters in sp_executesql offers the advantages to using the EXECUTE statement to execute a string: Because the actual text of the T-SQL in the sp_executesql string does not change between executions, the query optimizer will probably match the T-SQL statement in the second execution with the execution plan generated for the first execution. Therefore, SQL Server does not have to compile the second statement.
One of the examples on TechNet is pretty good at demonstrating parameter returns and parameter substitution. It also uses AdventureWorks2012, which you can download here.
DECLARE @IntVariable int; DECLARE @SQLString nvarchar(500); DECLARE @ParmDefinition nvarchar(500); DECLARE @max_title varchar(30); SET @IntVariable = 197; SET @SQLString = N'SELECT @max_titleOUT = max(JobTitle) FROM AdventureWorks2012.HumanResources.Employee WHERE BusinessEntityID = @level'; SET @ParmDefinition = N'@level tinyint, @max_titleOUT varchar(30) OUTPUT'; EXECUTE sp_executesql @SQLString, @ParmDefinition, @level = @IntVariable, @max_titleOUT=@max_title OUTPUT; SELECT @max_title;
A few things worth pointing out from the script above is that you can set the parameters (like in the example above) by declaring the @ParmDefinition and then setting it to include the parameters. Or, if there are not many parameters like the example above, just add them when you are executing sp_executesql. I’ve also moved the DECLARE@max_title to just before it is actually used.To me this looks a little neater and a little clearer to follow:
DECLARE @IntVariable int; DECLARE @SQLString nvarchar(500); SET @IntVariable = 197; SET @SQLString = N'SELECT @max_titleOUT = max(JobTitle) FROM AdventureWorks2012.HumanResources.Employee WHERE BusinessEntityID = @level'; DECLARE @max_title varchar(30); EXECUTE sp_executesql @SQLString, @level = @IntVariable, @max_titleOUT=@max_title OUTPUT, N'@level tinyint, @max_titleOUT varchar(30) OUTPUT' SELECT @max_title;
By using the parameter OUTPUT to store the result set generated by the SELECT statement in the @SQLString parameter we can use the @max_title parameter in other queries.
DECLARE @IntVariable int; DECLARE @SQLString nvarchar(500); SET @IntVariable = 197; SET @SQLString = N'SELECT @max_titleOUT = max(JobTitle) FROM AdventureWorks2012.HumanResources.Employee WHERE BusinessEntityID = @level'; DECLARE @max_title varchar(30); EXECUTE sp_executesql @SQLString, @level = @IntVariable, @max_titleOUT=@max_title OUTPUT, N'@level tinyint, @max_titleOUT varchar(30) OUTPUT' SELECT @max_title; select max (JobTitle) FROM AdventureWorks2012.HumanResources.Employee WHERE JobTitle = @max_title select BusinessEntityID FROM AdventureWorks2012.HumanResources.Employee WHERE JobTitle = @max_title
Although the order of the parameters is not important, you may cause this error if you try to add any further parameters in a form different from the @a_param = @b_param.
Must pass parameter number 4 and subsequent parameters as ‘@name = value’. After the form ‘@name = value’ has been used, all subsequent parameters must be passed in the form ‘@name = value’.
To make it a little clearer, the example below has moved the parameter definition after we have set the parameter substitution. In this order the sp_executesql will not work and the error above will appear.
DECLARE @IntVariable int; DECLARE @SQLString nvarchar(500); SET @IntVariable = 197; SET @SQLString = N'SELECT @max_titleOUT = max(JobTitle) FROM AdventureWorks2012.HumanResources.Employee WHERE BusinessEntityID = @level'; DECLARE @max_title varchar(30); EXECUTE sp_executesql @SQLString, @level = @IntVariable, @max_titleOUT=@max_title OUTPUT, N'@level tinyint, @max_titleOUT varchar(30) OUTPUT' SELECT @max_title;
Learning about sp_executesql and it’s benefits over EXEC@SQLString statement will be a big help to me. Microsoft recommend recommend that you use the sp_executesql stored procedure instead of the EXECUTE statement. From now on I’m definitely going to be using it.
