nvarchar(max) slowing stored procedure - c#

From yesterday, i'm facing a problem:when i call a stored proc from c#,it lasts >5 in, but when i execute it directly from SSMS (in the server machine) its lasts less than 30 seconds.
I have searched in forums and went trough this great article http://www.sommarskog.se/query-plan-mysteries.html but no result.
The script contained in my proc is retrieving 10 columns among them a column called "article" of type nvarchar(max).
When i remove the article column from my Select ,my proc executes quickly.
To further my logic, i have created a new stored proc retrieving just Primary Key Column and nvarchar(max) column.
I'm reproducing the same behaviour.Here is my new proc=MyNewProc(lasts >5 min when called from c# and 0 Secondes in the server from SSMS)
CREATE PROCEDURE Student.GetStudents
AS
BEGIN
SET NOCOUNT ON
-----------------
SELECT StudentId,Article
FROM Students
WHERE Degree=1
END
MyNewProc returns just 2500 rows.
Is that normal? How can i improve that.
SELECT SUM(DATALENGTH(Article)) FROM Students WHERE Degree=1
the result is 13885838

You're probably transferring a lot of data over the network. That takes time.
Instead of returning article try returning LEFT(article, 50) to see if its an issue with the volume of data or not.
One thing to note is that SSMS will begin populating the results immediately while a C# application probably will not.

In SSMS, go to the following: Tools -> Options
Then go to Query Execution -> SQL Server -> Advanced
From here, look at what check boxes are checked and if there is something that is checked, SSMS will use this automatically when you execute a sproc from inside of it but when you execute it from C# (or whatever client you're using) it won't be used.
I had this same issue and found out that I needed to include the following line at the top of my sproc and it worked perfectly:
SET ARITHABORT ON;

Related

How to write thread safe SQL Server stored procedures

I have some stored procedures in which multiple queries are being executed. To get last identity of insert I am using IDENT_CURRENT which is causuing problem.
My question is can I have lock statements like C# in T-SQL so that it can be thread safe?
EDIT: Code I am using
INSERT INTO activities
(creator
,title
,description)
VALUES
(#Creator
,#Tile
,#Description)
SET #ActivityId = IDENT_CURRENT('dbo.activities');
INSERT INTO [dbo].activity_cfs
([activity_id],
[cf_id],
[cf_field_name],
[field_key],
[value])
SELECT
#ActivityId,
cf_id,
cf_field_name,
field_key,
value
FROM #ActivityCustomFields
#ActivityCustomFields is my temp table.
It is quite likely that you should use SCOPE_IDENTITY instead of IDENT_CURRENT. There are many explanations how they differ, for example: What is the difference between Scope_Identity(), Identity(), ##Identity, and Ident_Current?
But, if you really need to guarantee that certain part of the code is not being run by several threads at the same time, you can use sp_getapplock.
Based on the code that you added to the question I'm now pretty sure that you should simply use SCOPE_IDENTITY, like this:
INSERT INTO activities
(creator
,title
,description)
VALUES
(#Creator
,#Tile
,#Description);
SET #ActivityId = SCOPE_IDENTITY();
INSERT INTO [dbo].activity_cfs
([activity_id],
[cf_id],
[cf_field_name],
[field_key],
[value])
SELECT
#ActivityId,
cf_id,
cf_field_name,
field_key,
value
FROM #ActivityCustomFields;
The SCOPE_IDENTITY function returns the last identity created in the same session and the same scope.
The IDENT_CURRENT returns the last identity created for a specific table or view in any session. So, if you have several threads that run this procedure simultaneously IDENT_CURRENT will see identities generated in other threads, which you don't want.
SCOPE_IDENTITY would be the way to go in my understanding. Please follow following link that in SQL Server when using SCOPE_IDENTITY will always be threadsafe:
http://www.vbforums.com/showthread.php?727047-RESOLVED-Is-MSSQL-s-SCOPE_IDENTITY()-thread-safe

LINQ to SQL - Stored Procedure Return Type Error

On SQL 2005 I have a simple SP that selects rows from a table. The SP does not use temporary tables or return multiple result sets. In VS2010 I drag the SP from the Server Explorer to the DBML designer and the SP is added to the LINQ data context. Everything works okay. If I then script the table and SP on to another live SQL 2005 server I am getting the error "The return types for the following stored procedures could not be detected .. " Like I say, no temp tables or multiple result sets that would typically produce this error. Could there be something else on the server causing this?
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[spUsers]
AS
BEGIN
SET NOCOUNT ON;
SELECT top 100 * from Users
END
Make sure you have:
SET NOCOUNT ON;
as the first line in your SP after the 'BEGIN' statement.
If your SP does not have this, then it returns messages like
'10 Rows affected...'
Which Linq tries to interprete as part of the function result. Iv'e had it bite me loads of times!!
This is a known issue with Linq to SQL, and a frustrating one. It also seems to be somewhat unpredictable. The only way I have gotten around this, reliably, is by not having L2S call a stored procedure when I need it to return a result set. I have it call a table-valued UDF instead. L2S seems to have no problems with these, and I've never experienced any problems using them.
Okay, the reason it was failing on the live server is to do with privilages needed to access the meta-data on the DBMS. These are needed to create the return value data type from the SP. By elevating the SQL user account and then dragging the SP on onto the DBML designer .. bingo .. it works!
Use
create procedure Getxxxxxxx
#xxxxx uniqueidentifier
,#xxxxx uniqueidentifier
set fmtonly off
//you query
set fmtonly on

Dataset times out while query runs immediately

I am running a query directly, it is trivial in nature:
SELECT * FROM [dbo].[vwUnloadedJobDetailsWithData] WHERE JobId = 36963
When I run this from Management studio the query doesn't even take a second. When I run it from within the table adapter it times out. I have fixed this multiple times, but the fix is ludicrous. If I delete the table adapter from my xsd file and recreate it the query time matches that of management studio for about two days, but I have to redeploy which is asinine.
Any insight into what could be causing this would be greatly appreciated. I've seen another question about this but the solution involving set arithabort on before the query had no effect for me.
Edit: It was asked that I show my code for calling the query. Now this happens when I go into my xsd file and just do preview data as well, but for sake of clarity, here it is:
using (TEAMSConnection connection = new TEAMSConnection())
{
connection.OpenConnection();
_JobDetailsDAO jobDetailDao= new _JobDetailsDAO(connection);
return jobDetailDao.GetUnloadedJobDetailsByJobId(jobId);
}
On disposal of connection the database connection is closed. using this line of code:
if (_DBConnection != null && _DBConnection.State == ConnectionState.Open)
_DBConnection.Close();
Edit2: I ran a trace and here are the set options that are being set
set quoted_identifier on
set arithabort off
set numeric_roundabort off
set ansi_warnings on
set ansi_padding on
set ansi_nulls on
set concat_null_yields_null on
set cursor_close_on_commit off
set implicit_transactions off
set language us_english
set dateformat mdy
set datefirst 7
set transaction isolation level read committed
I went and added that to the query that I generated in management studio and it still ran in less than a second. I even copied the query exactly as in the trace.
exec sp_executesql N'SELECT * FROM [dbo].[vwUnloadedJobDetailsWithData] WHERE JobID = #JobId',N'#JobId int',#JobId=36963
and it is still less than a second return time. I am so very confused.
Thanks,
Josh
the most likely scenarion why this would be happening is the difference in SET options between ssms and ado.net. that difference causes (re)building of execution plans that might not be optimal.
Alright, well I could not find any solution that would continue to allow me to use the dataset, so I went straight to using the SqlDataAdapter in code rather than using the auto generated TableAdapters.
According to the trace it performs the exact same query, but so far it works. It may not in two days, but for now it works it seems.
Just trying to think loudly:
Maybe there is a lock caused by another process/person? Is there anybody who updates the same row at the same time? Is there anybody who opens the table from Management studio or Query Analyzer with Open Table feature and plays with the filters?
Try looking for locks using sp_who2
Some thoughts:
What I'd call parameter sniffing for stored proc. Try the OPTION (RECOMPILE) hint, so your sent SQL looks like this:
exec sp_executesql
N'SELECT *
FROM [dbo].[vwUnloadedJobDetailsWithData]
WHERE JobID = #JobId
OPTION (RECOMPILE)',
N'#JobId int',
#JobId=36963
Explanation: When a query plan is produced and cached, it may be a bad, atypical value. Say JobID is usually very selective, but for that one execution it's not. When you run the query the next plan the cached plan is wrong for the next selective JobId. A plan will be recompiled for various reasons, but the value on recompilation matters.
Otherwise, what is the exact datatype of Jobid? If it's smallint, then the column will be converted to int in the parameterised query. When using a constant it will be smallint. Make sure the type is defined correctly: this matters in SQL code.

SET NOCOUNT ON and reading messages using C# and ADO.NET

SET NOCOUNT ON stops the message that shows the count of the number of rows affected by a Transact-SQL statement or stored procedure from being returned as part of the result set.
a) How can you read these messages using C# and ADO.NET ( I assume C# code reading these messages is the same regardless of whether T-SQL statements were executed in a stored procedure, batch or… )?
b) Assuming stored procedure contains several statements, how can your C# code identify to what SQL statement does particular messages refer to?
Thank you
Informational messages (like the rows affected count info) are reported in ADO.Net through the SqlConnection.InfoMessage event. Add a delegate to the event and will be invoked whenever the server transmits an informational message (ie. any error message with severity bellow 10).
there is no way to associate informational messages like afffected count info with the source. You're going to have to do it based on knowledge of the logic and understand that the first message refers to the first update, the second message to the second update etc.
Relying on affected rows count in the client is generaly a bad practice. The many issues ORM layers like NHibernate and ADO.Net datasets have when SET NOCOUNT ON is turned on just shows how problematic this practice is.
Don't rely on it. Best practice is SET NOCOUNT ON (discussed with my question here)
When you load your datatable, use .Count.
Use an OUTPUT parameter to pass ##ROWCOUNT back (or as a dataset)
Take a look at this question and answers. You can't do (b) above without adding some code in your TSQL that captures the ##rowcount and outputs it in some manner (like a resultset that you could read from).
One option is in your stored procedure is to include variables that you will pass back statement counts. You can do by creating your procedure with the needed OUTPUT parameters.
FIRST SQL HERE
#FirstSQLCount = ##ROWCOUNT
SECOND SQL HERE
#SecondSQLCount = ##ROWCOUNT

Can I get the rowcount before executing a stored procedure?

I have some complex stored procedures that may return many thousands of rows, and take a long time to complete.
Is there any way to find out how many rows are going to be returned before the query executes and fetches the data?
This is with Visual Studio 2005, a Winforms application and SQL Server 2005.
You mentioned your stored procedures take a long time to complete. Is the majority of the time taken up during the process of selecting the rows from the database or returning the rows to the caller?
If it is the latter, maybe you can create a mirror version of your SP that just gets the count instead of the actual rows. If it is the former, well, there isn't really that much you can do since it is the act of finding the eligible rows which is slow.
A solution to your problem might be to re-write the stored procedure so that it limits the result set to some number, like:
SELECT TOP 1000 * FROM tblWHATEVER
in SQL Server, or
SELECT * FROM tblWHATEVER WHERE ROWNUM <= 1000
in Oracle. Or implement a paging solution so that the result set of each call is acceptably small.
make a stored proc to count the rows first.
SELECT COUNT(*) FROM table
Unless there's some aspect of the business logic of you app that allows calculating this, no. The database it going to have to do all the where & join logic to figure out how line rows, and that's the vast majority of the time spend in the SP.
You can't get the rowcount of a procedure without executing the procedure.
You could make a different procedure that accepts the same parameters, the purpose of which is to tell you how many rows the other procedure should return. However, the steps required by this procedure would normally be so similar to those of the main procedure that it should take just about as long as just executing the main procedure.
You would have to write a different version of the stored procedure to get a row count. This one would probably be much faster because you could eliminate joining tables which you aren't filtered against, remove ordering, etc. For example if your stored proc executed the sql such as:
select firstname, lastname, email, orderdate from
customer inner join productorder on customer.customerid=productorder.productorderid
where orderdate>#orderdate order by lastname, firstname;
your counting version would be something like:
select count(*) from productorder where orderdate>#orderdate;
Not in general.
Through knowledge about the operation of the stored procedure, you may be able to get either an estimate or an accurate count (for instance, if the "core" or "base" table of the query is able to be quickly calculated, but it is complex joins and/or summaries which drive the time upwards).
But you would have to call the counting SP first and then the data SP or you could look at using a multiple result set SP.
It could take as long to get a row count as to get the actual data, so I wouldn't advodate performing a count in most cases.
Some possibilities:
1) Does SQL Server expose its query optimiser findings in some way? i.e. can you parse the query and then obtain an estimate of the rowcount? (I don't know SQL Server).
2) Perhaps based on the criteria the user gives you can perform some estimations of your own. For example, if the user enters 'S%' in the customer surname field to query orders you could determine that that matches 7% (say) of the customer records, and extrapolate that the query may return about 7% of the order records.
Going on what Tony Andrews said in his answer, you can get an estimated query plan of the call to your query with:
SET showplan_text OFF
GO
SET showplan_all on
GO
--Replace with call you your stored procedure
select * from MyTable
GO
SET showplan_all ofF
GO
This should return a table, or many tables which will let you get the estimated row count of your query.
You need to analyze the returned data set, to determine what is a logical, (meaningful) primary key for the result set that is being returned. In general this WILL be much faster than the complete procedure, because the server is not constructing a result set from data in all the columns of each row of each table, it is simply counting the rows... In general, it may not even need to read the actual table rows off disk to do this, it may simply need to count index nodes...
Then write another SQL statement that only includes the tables necessary to generate those key columns (Hopefully this is a subset of the tables in the main sql query), and the same where clause with the same filtering predicate values...
Then add another Optional parameter to the Stored Proc called, say, #CountsOnly, with a default of false (0) as so...
Alter Procedure <storedProcName>
#param1 Type,
-- Other current params
#CountsOnly TinyInt = 0
As
Set NoCount On
If #CountsOnly = 1
Select Count(*)
From TableA A
Join TableB B On etc. etc...
Where < here put all Filtering predicates >
Else
<Here put old SQL That returns complete resultset with all data>
Return 0
You can then just call the same stored proc with #CountsOnly set equal to 1 to just get the count of records. Old code that calls the proc would still function as it used to, since the parameter value is set to default to false (0), if it is not included
It's at least technically possible to run a procedure that puts the result set in a temporary table. Then you can find the number of rows before you move the data from server to application and would save having to create the result set twice.
But I doubt it's worth the trouble unless creating the result set takes a very long time, and in that case it may be big enough that the temp table would be a problem. Almost certainly the time to move the big table over the network will be many times what is needed to create it.

Categories

Resources