Echelon
Echelon

Reputation: 255

SqlCommand.ExecuteReader became very slow

I am using this code, to call an SQL function, which returns entries from an SQL Server database table

string cmd = String.Format("select * from dbo.GetData(@userId, @fileId, @created);");

using (SqlConnection conn = new SqlConnection(connectionString))
{
    if (conn.State != ConnectionState.Open)
        conn.Open();

    SqlCommand command = new SqlCommand(cmd, conn);

    if (String.IsNullOrEmpty(userId))
        command.Parameters.AddWithValue("@userId", DBNull.Value);
    else
        command.Parameters.AddWithValue("@userId", userId);

    if (String.IsNullOrEmpty(fileId))
        command.Parameters.AddWithValue("@fileId", DBNull.Value);
    else
        command.Parameters.AddWithValue("@fileId", docId);

    command.Parameters.AddWithValue("@created", created);

    internalWatch.Reset();
    internalWatch.Start();

    IDataReader reader = command.ExecuteReader();                
    table = GetDataTableFromDataReader(reader);

    reader.Close();
    reader.Dispose();

    conn.Close();

    internalWatch.Stop();

The table I am working with contains a bit more than 1.5 million entries and should return a bit more than 250k entries.

If I am executing the SQL function within SSMS it needs 8 seconds to return the results and I already used the code above last week to get the results within my desktop application. At this time everything was fine. The code needed between 10-12 seconds to get the results.

The strange thing is, that today the code needs more than 40 seconds to return the same results, but I haven't change anything within the SQL function or the code itself. The only change I did in my programm is adding a few more classes, which have nothing to do with the code above.

If I am debugging the code, I can see, that the line

IDataReader reader = command.ExecuteReader();

needs the most of the time now.

Since I haven't change anything in the SQL function or the code itself, I can't understand why it is taking so long now...

And if needed, here is the SQL function, I am using:

ALTER FUNCTION [dbo].[GetData]
(@userId varchar(128) = NULL, 
 @fileId varchar(192) = NULL, 
 @created DateTimeOffset(7))
RETURNS TABLE 
AS
    RETURN (
        WITH FindNewestVersion AS
        (
            SELECT
                *, 
                ROW_NUMBER() OVER (PARTITINO BY FileId, UserId 
                                   ORDER BY created DESC) rn 
            FROM
                table1
        )
        SELECT        
            q.Created, q.Updated, q.FileId, q.UserId, 
            F.column1, F.column2, F.column3
        FROM            
            table2 AS F 
        INNER JOIN
            table1 AS q ON F.column4 = q.PersonId AND F.created = q.created
        INNER JOIN
            (SELECT 
                 created, PersonId, DocumentId 
             FROM
                 FindNewestVersion 
             WHERE
                 rn = 1) AS x ON q.created = x.created 
                              AND q.PersonId = x.PersonId 
                              AND q.FileId = x.FileId

        WHERE        
            (F.column1 = 'Sample') 
            AND (q.Created <= @created) 
            AND (q.Updated >= @created)
            AND Q.PersonId = ISNULL(@userId, Q.PersonId)
            AND Q.FileId = ISNULL(@fileId, Q.FileId)
)

Thank you for any suggestions!

Upvotes: 4

Views: 5239

Answers (2)

Karan Singh
Karan Singh

Reputation: 1

This seems like a case of SET ARITHABORT ON

By default in SQL it's On But while we execute Through C# Code then its not understand the ARITHABORT ON.

One thing that you could do is rewrite your procedure as follows:

DROP FUNCTION [dbo].[GetData]
CREATE PROCEDURE[dbo].[GetData]

(
@userId varchar(128) = NULL, 
@fileId varchar(192) = NULL, 
@created DateTimeOffset(7)
)

RETURNS TABLE 
AS

SET ARITHABORT ON 

DECLARE @l_userId varchar(128) = NULL, 
DECLARE @l_fileId varchar(192) = NULL, 
DECLARE @l_created DateTimeOffset(7)

SET @l_userId = userId
SET @l_fileId = fileId
SET @l_created = @created



(

WITH FindNewestVersion as
(
    Select *, ROW_NUMBER() 
    over (partition by FileId, UserId ORDER BY created DESC)rn from table1
)


SELECT        q.Created, q.Updated, q.FileId, q.UserId, 
              F.column1, F.column2, F.column3
FROM            table2 AS F INNER JOIN
                table1 AS q
                ON F.column4 = q.PersonId AND F.created = q.created
                INNER JOIN
                (
                    select created, PersonId, DocumentId from FindNewestVersion where rn = 1
                ) AS x ON q.created = x.created AND q.PersonId = x.PersonId AND q.FileId = x.FileId

WHERE        (F.column1 = 'Sample') AND (q.Created <= @created) AND (q.Updated >= @created)
And Q.PersonId = ISNULL(@l_userId, Q.PersonId)
And Q.FileId = ISNULL(@l_fileId, Q.FileId)
)`enter code here`

Upvotes: 0

DB101
DB101

Reputation: 633

This seems like a case of parameter sniffing

One thing that you could do is rewrite your procedure as follows:

`

DROP FUNCTION [dbo].[GetData]
CREATE PROCEDURE[dbo].[GetData]

(
@userId varchar(128) = NULL, 
@fileId varchar(192) = NULL, 
@created DateTimeOffset(7)
)

RETURNS TABLE 
AS
DECLARE @l_userId varchar(128) = NULL, 
DECLARE @l_fileId varchar(192) = NULL, 
DECLARE @l_created DateTimeOffset(7)

SET @l_userId = userId
SET @l_fileId = fileId
SET @l_created = @created



(

WITH FindNewestVersion as
(
    Select *, ROW_NUMBER() 
    over (partition by FileId, UserId ORDER BY created DESC)rn from table1
)


SELECT        q.Created, q.Updated, q.FileId, q.UserId, 
              F.column1, F.column2, F.column3
FROM            table2 AS F INNER JOIN
                table1 AS q
                ON F.column4 = q.PersonId AND F.created = q.created
                INNER JOIN
                (
                    select created, PersonId, DocumentId from FindNewestVersion where rn = 1
                ) AS x ON q.created = x.created AND q.PersonId = x.PersonId AND q.FileId = x.FileId

WHERE        (F.column1 = 'Sample') AND (q.Created <= @created) AND (q.Updated >= @created)
And Q.PersonId = ISNULL(@l_userId, Q.PersonId)
And Q.FileId = ISNULL(@l_fileId, Q.FileId)
)

` You can then get you data as you would by calling a stored procedure.

Upvotes: 3

Related Questions