SqlCommand.ExecuteReader变得非常慢

时间:2016-06-17 09:34:14

标签: sql-server

我正在使用此代码调用SQL函数,该函数返回SQL Server数据库表中的条目

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();

我正在使用的表包含超过150万个条目,并且应该返回超过250,000个条目。

如果我在SSMS中执行SQL函数,则需要8秒才能返回结果,并且我已经使用上周的代码在我的桌面应用程序中获取结果。这时一切都很好。代码需要10-12秒才能得到结果。

奇怪的是,今天代码需要超过40秒才能返回相同的结果,但我还没有改变SQL函数或代码本身的任何内容。我在我的程序中做的唯一改变是添加了一些类,这与上面的代码无关。

如果我正在调试代码,我可以看到,该行

IDataReader reader = command.ExecuteReader();

现在需要大部分时间。

由于我没有改变SQL函数或代码本身的任何内容,我无法理解为什么它需要这么长时间......

如果需要,这是SQL函数,我正在使用:

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)
)

感谢您的任何建议!

2 个答案:

答案 0 :(得分:3)

这似乎是参数嗅探的一种情况

您可以做的一件事是按以下步骤重写您的程序:

`

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)
)

` 然后,您可以通过调用存储过程来获取数据。

答案 1 :(得分:0)

这似乎是SET ARITHABORT ON的情况

SQL默认情况下为On,但是当我们通过C#代码执行时,它不了解ARITHABORT ON。

您可以做的一件事是如下重写程序:

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`