I have a simple stored procedure that has parameter
CREATE Procedure GetSupplierForTesting
(#SupplierId INT)
AS
SELECT SuppLabel
FROM Supplier
WHERE Supplier.SupplierId = #SupplierId
I am able to call it with the exec command inside another stored procedure like this
exec GetSupplierForTesting #SupplierId = 10
I came across an article that explains how sp_executesql is faster than exec. My problem is that I don't know how to call a stored procedure that has parameters with sp_executesql. I have tried this code
DECLARE #SupplierId INT = 10;
EXEC sp_executesql N'GetSupplierForTesting', N'#SupplierId INT', #SupplierId
but I am getting an error:
Procedure or function 'GetSupplierForTesting' expects parameter '#SupplierId', which was not supplied
The syntax you would need would be
DECLARE #SupplierId INT = 10;
EXEC sys.sp_executesql N'GetSupplierForTesting #SupplierId=#SupplierId',
N'#SupplierId INT',
#SupplierId=#SupplierId
But don't do this. It is utterly pointless. There is no magic performance increase to be expected from using sp_executesql to basically wrap the same exec statement and execute it at a different scope.
Just use
exec dbo.GetSupplierForTesting #SupplierId = 10
1
Performance issue is based on assumption that when you are using non-parametrized ad-hoc query, it (the string representing this query) will be different every time - because specific argument values are parts of query text.
Whilst parametrized query keeps it's body unchanged because in place of where ... and title="asdf" you have where ... and title = #title. Only contents of variable #title change. But query text persists and sql server realizes that there is no need to recompile it.
Non-parametrized query will be recompiled every time you change values used in it.
2
You are getting exception because your script does not pass any arguments to the stored proc.
Your script is: 'GetSupplierForTesting' - that's it.
By passing arguments to sp_executesql you are passing them to the scipt. Not to the sp used in script, but to the script itself. E.g.:
exec sp_executesql N'print #val', N'#val int', #val = 1
this script does utilize variable #val. Your - does not. It just contains name of the proc. So your script corrected should look like
exec sp_executesql
N'exec GetSupplierForTesting #Supplier = #Supplier_id_value',
N'#Supplier_id_value int',
#Supplier_id_value = 10
script contains code calling your sp and this code passes argument to sp with value taken from #Supplier_id_value variable
#Supplier_id_value is declared as int for internals of this script
value of 10 is passed to the argument of the script
3
Performance issue you are talking about is about ad-hocs, not SPs.
Another face of this issue is parameter sniffing problem. Sometimes with specific param values your script or SP should use another execution plan, different from the plan it used for previously passed param values.
Every time recompiled ("slowly executed") ad-hoc would be (re)compiled for sure and probably would get better execution plan while SP or parametrized query would probably not be recompiled and would use worse, less optimal execution plan and would finally perform much slower than "slow because of recompilation" ad-hoc query.
There are no "write this - and it will work slowly", "write that - and it will hurtle like a rocket" rules in sql. It all depends on many factors. Sometimes one would probably need specifically ad-hocs, sometimes - should avoid them totally.
Related
I have a recursive procedure "FindLoopMakingNode" which is used to find loops, and I'm not expecting nested level to be more than 30.
Here is the recursive procedure:
alter procedure FindLoopMakingNode
#p1 int,
#p2 nvarchar(max)
as
begin
-- recursively calls itself
end
Also, I have another procedure "CheckFormulaForLoop" which is responsible for finding every kind of loop and it uses the recursive procedure mentioned above as well as other statements.
Here is the main wrapper
alter procedure CheckFormulaForLoop
#p1 int,
#p2 nvarchar(max),
#p3 bit
as
begin
--search for other kinds of loop
--if no other loop exists calls recursive procedure
EXEC dbo.FindLoopMakingNode #p1,#p2
--writes the result in a temp table which has been created by wrapper procedure
end
Because I use the second proc for different scenarios, I have different wrapper procedures which use it
Here is the problem: when I execute "CheckFormulaForLoop" for a given set of parameters there is no problem but when I execute one of those wrapper procedures for the exact same set of parameters, I get the Error blew:
Maximum stored procedure, function, trigger, or view nesting level exceeded (limit 32)
Here is the wrapper(the one which throws exception, and yes, it's really that simple)
alter procedure CheckFormulaForLoopWrapper
#p1 int,
#p2 nvarchar(max),
#p3 bit
as
begin
Create table #tempLoopHolder(id int,code int)
EXEC CheckFormulaForLoop #p1,#p2,#p3
SELECT id,code from #tempLoopHolder
end
Now when I run
Execute CheckFormulaForLoopWrapper 1212,'2',1
It throws the exception but when I run
Create table #tempLoopHolder(id int,code int)
EXEC CheckFormulaForLoop 1212,'2',1
SELECT id,code from #tempLoopHolder
it runs successfully
I'm wondering if there is a problem with recursion levels why sql doesn't throw an exception when running the main procedure? and can nesting be responsible for this error?
You may maintain this by adding one more parameter to keep the count of recursive call.
You can pass a parameter and before executing your procedure check the value, in your case <30. For true condition before executing your procedure increment your value by +1. By this way you can keep the track of how many times your recursive call is made.
Sample code for same will be like this
Create proc Calltab ( #id int, #cou int=0 )
as
Begin
if(#cou <30 )
Begin
--- perform your operation in this section
set #cou = #cou + 1 --- here increment your value by +1 for the count
exec CallTab #id, #cou
end
end
GO
You may test the same with this
exec CallTab 1 , 0
I have to thank all of you for your precious helps
using all your tips, I found That using a combination of nested procedures + recursive procedure is my problem since as #Zohar Peled said :
The wrapper procedure adds another nesting level. In SQL Server, a procedure is considered nested when it's called by another procedure. As you can surly understand from the error message, using a stored procedure recursively is probably not the best way to handle whatever situation you need handling. If the soul purpose of this procedure is to fill a temporary table, there are probably better ways to do that then recursion. You might want to ask a different question on how to implement whatever it is that procedure does in a set based approach (which is SQL's strong suit).
I used ##NESTLEVEL that #Dale Burrell had mentioned and it turned out that my recursive procedure had to run 31 times (for given parameters) and this amount + 2 ( for 2 wrappers) led to the exception
So finally I see no way to fix it but trying to find a set based approach instead of recursion
I would like to know if the following is an error in our setup or a bug in SQL Server: if we run a certain stored procedure with three parameters, it takes about 3 minutes.
CREATE PROCEDURE [dbo].[ourProcedure]
#param1 INT,
#param2 INT,
#param3 DATETIME
AS
BEGIN...
If we run the same procedure, but in the creation we have created local copies of the parameters, it takes only 11 seconds!
CREATE PROCEDURE [dbo].[ourProcedure]
#param1_x INT,
#param2_x INT,
#param3_x DATETIME
AS
BEGIN
DECLARE #param1 INT
DECLARE #param2 INT
DECLARE #param3 DATETIME
#param1 = #param1_x
#param2 = #param2_x
#param3 = #param3_x
...
Can someone tell my WHY? Why doesn't SQL Server handle parameters like C#?
This is what is usually called "parameter sniffing". The thing is that SQL Server optimizer uses values of the parameters along with internal statistical information on distribution of values to estimate cardinalities for the values passed into the procedure, and produce an execution plan. However, this can go wrong for many reasons. Other point to make is that the execution plans are cached so the procedure is being optimized just the first time it's executed. If parameter used for optimizing the procedure are different enough from the current ones it could lead to bad performance.
Optimizer cannot use values of variables to do the same, because the variable assignement is part of the very same batch that's being optimized and not known beforehand. In this case it uses heuristics and averages which results probably in different execution plan.
In short, you have two different execution plans here, the first one also probably optimized for different set of parameter values.
I have strange issue in my win-form application where I am calling a stored procedure and it takes about 6 seconds to execute.
(This stored procedure accepts several parameters including one output parameter)
From application level I used :
Dim dt1 = DateTime.Now
cmd.ExecuteNonQuery()
MessageBox.Show(DateTime.Now.Subtract(dt1).Seconds)
This is about 5-6 seconds
I have tried running the same stored procedure with same parameters on sql-server and it takes no time to execute:
declare #val decimal
exec mysp 'value1','value2','value3','value4',#val out
select #val
I am not sure what the issue is or where to start.
The issue with difference between calling SP directly and from .NET code, maybe due to parameter sniffing. SQL Server maybe caching execution plan that is not optimal for the parameters you're passing from code.
To avoid this try adding WITH RECOMPILE to your SP definition, e.g.
CREATE PROCEDURE MySP (
... parameters...
) WITH RECOMPILE
AS
BEGIN
...
It could be an issue if your stored procedure expects say nvarchar and you use a varchar. SQL server will accept the parameters but will be forced to do a conversion. Do you have more specifics?
I would like to call a stored procedure or user-defined function that returns a dynamic table that is created via a pivot expression. I don't know the number of columns up front.
Is this possible? (I am not interested in temporary tables)
You can do that via stored procedure as it can return any kind of table, question is what are you trying to achieve and what will you do with data that you have no idea about?
This cannot be done with functions (as the returned table structure must be pre-defined), but it can be done with a stored proceed. Some psuedo-code:
CREATE PROCEDURE Foo
As
DECLARE #Command
SET #Command = 'SELECT * from MyTable'
-- For debugging, work in an optional PRINT #Command statement
EXECUTE (#Command)
RETURN 0
When you run stored procedure Foo, it builds your query as a string in #Command, and then dynamically executes it without knowing anything about what is being queried or returned, and the data set returned by that EXECUTE statement is "passed back" to the process that called the procedures.
Build your query with care, this stuff can be really hard to debug. Depending on your implementation, it might be a source of SQL injection attacks (remember, the stored procedure really doesn't know what that dynamic query is going to do). For quick stuff, EXECUTE() works fine, but for safer and more useful (if elaborate) solutions, look into sp_ExecuteSQL.
Yes, you can do this from a Stored Procedure, but not from a user-defined Function. It is worth looking into the Table Value Function, I believe you can also return a dynamic table from there, but I have not used that myself.
I've got a search screen on which the user can specify any combination of first name, last name, semester, or course. I'm not sure how to optimally code the SQL Server 2005 stored procedure to handle these potentially optional parameters. What's the most efficient way? Separate procedures for each combination? Taking the items in as nullable parms and building dynamic SQL?
I'd set each parameter to optional (default value being null)
and then handle it in the WHERE....
FirstName=ISNULL(#FirstName,FirstName)
AND
LastName=ISNULL(#LastName,LastName)
AND
SemesterID=ISNULL(#SemesterID,SemesterID)
That'll handle only first name, only last name, all three, etc., etc.
It's also a lot more pretty/manageable/robust than building the SQL string dynamically and executing that.
The best solution is to utilize sp_execute_sql. For example:
--BEGIN SQL
declare #sql nvarchar(4000)
set #sql =
'select * from weblogs.dbo.vwlogs
where Log_time between #BeginDate and #EndDate'
+ case when #UserName is null then '' else 'and client_user = #UserName' end
sp_execute_sql
#sql
, #params = '#UserName varchar(50)'
, #UserName = #UserName
--END SQL
As muerte mentioned, this will have a performance benefit versus exec()'ing a similar statement.
I would do it with sp_executesql because the plan will be cached just for the first pattern, or the first set of conditions.
Take a look at this TechNet article:
sp_executesql can be used instead of stored procedures to execute a Transact-SQL statement many times when the change in parameter values to the statement is the only variation. Because the Transact-SQL statement itself remains constant and only the parameter values change, the SQL Server query optimizer is likely to reuse the execution plan it generates for the first execution.
Was just posting the same concept as Kevin Fairchild, that is how we typically handle it.
You could do dynamic sql in your code to create the statement as required but if so you need to watch for sql injection.
As muerte points out, the plan will be cached for the first set of parameters. This can lead to bad performance when its run each subsequent time using alternate parameters. To resolve that use the WITH RECOMPILE option on the procedure.