Slow in SSRS but fast in SSMS - sql

I have a query that runs fast in SSMS but runs very slow in SSRS and As I was searching for a solution, I came about this solution below provided by user275554
"Thanks for the suggestions provided here. We have found a solution and it did turn out to be related to the parameters. SQL Server was producing a convoluted execution plan when executed from the SSRS report due to 'parameter sniffing'. The workaround was to declare variables inside of the stored procedure and assign the incoming parameters to the variables. Then the query used the variables rather than the parameters. This caused the query to perform consistently whether called from SQL Server Manager or through the SSRS report".
My Problem is that I tried assigning parameters to the variables but it seems I really don't know how to do it so the report didn't produce any data.
An example of what I tried is this:
CREATE PROC MissingData
AS
DECLARE #Office varchar (200)
DECLARE #employee varchar (100)
SET #Office = #Office -- #office is the parameter from SSRS
SET #employee = #employee-- #employee is the parameter FROM SSRS
Can someone help me on how to assign the parameter to use the variables as provided by the solution.
Thanks
Mi

See this article for a good explanation of parameter sniffing, performance problems and solutions.
Since it's a reporting procedure my guess is the easiest way to get around this is to add WITH RECOMPILE to the CREATE PROC statement, which means SQL Server will recompile the proc each time it's run and therefore will work out the best query plan based on the parameters you're calling it with, rather than parameters you previously called it with.

This is the right format to pass the parameters to the Stored Proc and then create the local variables to prevent parameter sniffing. As was mentioned, you can also add WITH RECOMPILE
CREATE PROC MissingData
#Office varchar(200)
,#employee varchar(100)
WITH RECOMPILE
AS
DECLARE #ramOffice varchar (200)
DECLARE #ramEmployee varchar (100)
SET #ramOffice = #Office -- #office is the parameter from SSRS
SET #ramemployee = #employee-- #employee is the parameter FROM SSRS

Related

How to use OPENROWSET to execute a stored procedure with parameters and insert result into a temp table

I want to insert the results of a stored procedure into a temp table using OPENROWSET. However, the issue I run into is I'm not able to pass parameters to my stored procedure.
This is my stored procedure:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[N_spRetrieveStatement]
#PeopleCodeId nvarchar(10),
#StatementNumber int
AS
SET NOCOUNT ON
DECLARE #PersonId int
SELECT #PersonId = [dbo].[fnGetPersonId](#PeopleCodeId)
SELECT *
INTO #tempSpRetrieveStatement
FROM OPENROWSET('SQLNCLI', 'Server=PCPRODDB01;Trusted_Connection=yes;',
'EXEC Campus.dbo.spRetrieveStatement #StatementNumber, #PersonId');
--2577, 15084
SELECT *
FROM #tempSpRetrieveStatement;
OpenRowSet will not allow you to execute Procedure with input parameters. You have to use INSERT/EXEC.
INTO #tempSpRetrieveStatement(Col1, Col2,...)
EXEC PCPRODDB01.Campus.dbo.spRetrieveStatement #StatementNumber, #PersonId
Create and test a LinkedServer for PCPRODDB01 before running the above command.
The root of your problem is that you don't actually have parameters inside your statement that you're transmitting to the remote server you're connecting to, given the code sample you provided. Even if it was the very same machine you were connecting to, they'd be in different processes, and the other process doesn't have access to your session variables.
LinkedServer was mentioned as an option, and my understanding is that's the preferred option. However in practice that's not always available due to local quirks in tech or organizational constraints. It happens.
But there is a way to do this.
It's hiding in plain sight.
You need to pass literals into the string that will be executed on the other server, right?
So, you start by building the string that will do that.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[N_spRetrieveStatement]
#PeopleCodeId nvarchar(10),
#StatementNumber int
AS
SET NOCOUNT ON
DECLARE
#PersonId INT,
#TempSQL VARCHAR(4000) = '';
SELECT #PersonId = [dbo].[fnGetPersonId](#PeopleCodeId);
SET #TempSQL =
'EXEC Campus.dbo.spRetrieveStatement(''''' +
FORMAT(#StatementNumber,'D') +''''', ''''' +
FORMAT(#PersonId,'D') + ''''')';
--2577, 15084
Note the seemingly excessive number of quotes. That's not a mistake -- that's foreshadowing. Because, yes, OPENROWSET hates taking variables as parameters. It, too, only wants literals. So, how do we give OPENROWSET what it needs?
We create a string that is the entire statement, no variables of any kind. And we execute that.
SET #TempSQL =
'SELECT * INTO #tempSpRetrieveStatement ' +
'FROM OPENROWSET(''SQLNCLI'', ''Server=PCPRODDB01;Trusted_Connection=yes;'', ' + #TempSQL +
'EXEC Campus.dbo.spRetrieveStatement #StatementNumber, #PersonId';
EXEC (#TempSQL);
SELECT *
FROM #tempSpRetrieveStatement;
And that's it! Pretty simple except for counting your escaped quotes, right?
Now... This is almost beyond the scope of the question you asked, but it is a 'gotcha' I've experienced in executing stored procedures in another machine via OPENROWSET. You're obviously used to using temp tables. This will fail if the stored procedure you're calling is creating temp tables or doing a few other things that -- in a nutshell -- inspire the terror of ambiguity into your SQL server. It doesn't like ambiguity. If that's the case, you'll see a message like this:
"Msg 11514, Level 16, State 1, Procedure sp_describe_first_result_set, Line 1
The metadata could not be determined because statement '…your remote EXEC statement here…' in procedure '…name of your local stored procedure here…' contains dynamic SQL. Consider using the WITH RESULT SETS clause to explicitly describe the result set."
So, what's up with that?
You don't just get data back with OPENROWSET. The local and remote servers have a short conversation about what exactly the local server is going to expect from the remote server (so it can optimize receiving and processing it as it comes in -- something that's extremely important for large rowsets). Starting with SQL Server 2012, sp_describe_first_result_set is the newer procedure for this, and normally it executes quickly without you noticing it. It's just that it's powers of divination aren't unlimited. Namely, it doesn't know how to get the type and name information regarding temp tables (and probably a few other things it can't do -- PIVOT in a select statement is probably right out).
I specifically wanted to be sure to point this out because of your reply regarding your hesitation about using LinkedServer. In fact, the very same reasons you're hesitant are likely to render that error message's suggestion completely useless -- you can't even predict what columns you're getting and in what order until you've got them.
I think what you're doing will work if, say, you're just branching upstream based on conditional statements and are executing one of several potential SELECT statements. I think it will work if you're just not confident that you can depend on the upstream component being fixed and are trying to ensure that even if it varies, this procedure doesn't have to because it's very generic.
But on the other hand you're facing a situation in which you literally cannot guarantee that SQL Server can predict the columns, you're likely going to have to force some changes in the stored procedure you're calling to insist that it's stable. You might, for instance work out how to ensure all possible fields are always present by using CASE expressions rather than any PIVOT. You might create a session table that's dedicated to housing what you need to SELECT just long enough to do that then DELETE the contents back out of there. You might change the way in which you transmit your data such that it's basically gone through the equivalent of UNPIVOT. And after all that extra work, maybe it'll be just a matter of preference if you use LinkedServer or OPENROWSET to port the data across.
So that's the answer to the literal question you asked, and one of the limits on what you can do with the answer.

SQL how to call user defined function by dynamic variable name

In SQL - I have list of user defined function names in a table. based on the logic i need to call/exec the function.
Please my high level code logic below,
DECLARE #MY_FUNCTION VARCHAR(1000);
DECLARE #MY_INPUT_PARAMETER INT;
DECLARE #MY_OUTPUT_PARAMETER INT;
SET #MY_FUNCTION = '' -- Dynamically function name will be provided based on some big logic
--Note: function has input and output parameter
--my query
-- call the function by #MY_FUNCTION (#MY_INPUT_PARAMETER )
#MY_OUTPUT_PARAMETER = EXEC #MY_FUNCTION (#MY_INPUT_PARAMETER)
--Some big sql script using #MY_OUTPUT_PARAMETER
(
-- Script goes here
)
You will need to construct the function with parameters inside the variable and then run sp_execute. Check out the samples in https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-executesql-transact-sql?view=sql-server-ver15#c-using-the-output-parameter
Important
However, try to avoid this method of execution if possible. Let the application decide what SP to call and the SP can then use the right function to make the call. There are two advantages to this.
Your SP will be compiled and SQL will be able to have an execution plan and continue to fine tune it. Hence, better performance
You will have less chances of SQL injections depending on how the table with functions are populated.

Can Table-Valued Parameters be temporary

I am trying to create a Stored Procedure that has a parameter which can take an unknown number of values. For this purpose I am using a Table-Valued Parameter that I can query. I was wondering if it is possible to have the Table-Valued Parameter be temporary and have it dropped after the Stored Procedure is executed? I tried reading up about it but from what I have found nowhere is explicitly stated whether the answer is 'Yes' or 'No'.
I'd be very grateful for any help I get. Thanks!
I am using SQL Server 2016.
I declare them as follows:
CREATE TYPE [schema].[tvp] AS Table ( value INT NULL)
GO
CREATE PROCEDURE [schema].[procedure] (
#Param [tvp] READONLY
) AS BEGIN ..
Table Valued Parameters are automatically temporary and will be dropped on the SQL Server after the stored procedure executes. On the .net side the parameter will also be dropped in normal "garbage handling".
Parameters are parameters - they only hold values temporarily. What applies to an int or a varchar parameter while calling a stored proc would apply to TVP as well.

How to capture stored procedure text when passing from Dephi 2006

HI all,
I have a large (100+) parameter list for my SP in my delphi code. This is for MS SQL Server 2005. For debugging purposes, I want to capture the text of the stored procedure command, so i can execute it on the SQL server and debug the SP. Is there a way i can capture what is exactly passed to the database? I thought about using a trace, and I'll try that tomorrow if this fails, but its cumbersome to set up and sift through and catch the SP.
Thanks
You should use the SQL Server Profiler for this. Start a new trace with default settings. Let it run while your client executes the SP. Stop the trace. Use ctrl-F and search for you SP name.
I normally don't care for playing with the programming environment.
Profiling would be a good option if you can identify the ClientProcessID (the PID showing in Task Manager of your client program) - that should narrow it down enough.
Another alternative I like is to simply capture it at the SQL Server end.
Sample proc
create proc takes3params
#a int, #b varchar(100), #c datetime
as
select #a, #b, #c
Becomes
alter proc takes3params
#a int, #b varchar(100), #c datetime
as
insert capture_takes3params(a,b,c) select #a, #b, #c -- << -- added
select #a, #b, #c
The support table is a mirror of the params, with 2 additional control columns
create table capture_takes3params(
id int identity primary key, captured datetime default(getdate()), -- control
a int, b varchar(100), c datetime
)
This doesn't work when the proc has defaults though.
EDIT
We use ADO to connect to MS SQL. Not sure what the alternative to 100+ params is, maybe pass table structures? Advice welcome! We are passing in HL7 messages which typically have 100 or so fields. –
Table valued parameters are only available from SQL Server 2008 onwards, from what I recall. That seems unwieldy as well from Delphi - I would instead look at a single XML parameter dissected in TSQL, which 2005+ has good support for.

SQL poor stored procedure execution plan performance - parameter sniffing

I have a stored procedure that accepts a date input that is later set to the current date if no value is passed in:
CREATE PROCEDURE MyProc
#MyDate DATETIME = NULL
AS
IF #MyDate IS NULL SET #MyDate = CURRENT_TIMESTAMP
-- Do Something using #MyDate
I'm having problems whereby if #MyDate is passed in as NULL when the stored procedure is first compiled, the performance is always terrible for all input values (NULL or otherwise), wheras if a date / the current date is passed in when the stored procedure is compiled performance is fine for all input values (NULL or otherwise).
What is also confusing is that the poor execution plan that is generated in is terrible even when the value of #MyDate used is actually NULL (and not set to CURRENT_TIMESTAMP by the IF statement)
I've discovered that disabling parameter sniffing (by spoofing the parameter) fixes my issue:
CREATE PROCEDURE MyProc
#MyDate DATETIME = NULL
AS
DECLARE #MyDate_Copy DATETIME
SET #MyDate_Copy = #MyDate
IF #MyDate_Copy IS NULL SET #MyDate_Copy = CURRENT_TIMESTAMP
-- Do Something using #MyDate_Copy
I know this is something to do with parameter sniffing, but all of the examples I've seen of "parameter sniffing gone bad" have involved the stored procedure being compiled with a non-representative parameter passed in, however here I'm seeing that the execution plan is terrible for all conceivable values that SQL server might think the parameter might take at the point where the statement is executed - NULL, CURRENT_TIMESTAMP or otherwise.
Has anyone got any insight into why this is happening?
Basically yes - parameter sniffing (in some patch levels of) SQL Server 2005 is badly broken. I have seen plans that effectively never complete (within hours on a small data set) even for small (few thousand rows) sets of data which complete in seconds once the parameters are masked. And this is in cases where the parameter has always been the same number. I would add that at the same time I was dealing with this, I found a lot of problems with LEFT JOIN/NULLs not completing and I replaced them with NOT IN or NOT EXISTS and this resolved the plan to something which would complete. Again, a (very poor) execution plan issue. At the time I was dealing with this, the DBAs would not give me SHOWPLAN access, and since I started masking every SP parameter, I've not had any further execution plan issues where I would have to dig in to this for non-completion.
In SQL Server 2008 you can use OPTIMIZE FOR UNKNOWN.
One way I was able to get around this problem in (SQL Server 2005) instead of just masking the parameters by redeclaring local parameters was to add query optimizer hints.
Here is a good blog post that talks more about it:
Parameter Sniffing in SqlServer 2005
I used: OPTION (optimize for (#p = '-1'))
Declare the procedure parameter inside the procedure and pass the external parameter to the internal .. compile ..