调用 SQL Server 存储过程的 SqlCommand 超时

Posted

技术标签:

【中文标题】调用 SQL Server 存储过程的 SqlCommand 超时【英文标题】:SqlCommand calling a SQL Server stored procedure times out 【发布时间】:2012-06-14 19:21:53 【问题描述】:

我有一个转换实用程序,基本上可以将值从一个表复制到另一个表。一段时间以来效果很好,但我遇到了一位客户的奇怪问题。他们使用该实用程序处理了 150 万条记录,但现在它已完全停止。

从 VB.Net 调用存储过程时,它只是挂起,直到 SqlCommand 超时。从 Management Studio 调用相同的存储过程会立即执行。我的 SqlCommand 的 VB.Net 代码如下(insertConn 是之前定义和打开的,dr 是一个 SqlDataReader,它已在上一步中从完全不同的 SqlConnection 和 SqlCommand 实例中填充):

Dim conn As New SqlConnection("connection string here")
Dim insertConn As New SqlConnection("connection string here")
Dim dr As SqlDataReader = Nothing
Dim readCommand As New SqlCommand("my query here", conn)
conn.Open()
insertConn.Open()
...
dr = readCommand.ExecuteReader()
...
While dr.Read()
    Using insertCommand = New SqlCommand("dmDocumentFieldInsert", insertConn)
        insertCommand.CommandType = CommandType.StoredProcedure

        insertCommand.Parameters.AddWithValue("@DocumentKey", dr("DocumentKey"))
        insertCommand.Parameters.AddWithValue("@FieldId", "TITLE")
        insertCommand.Parameters.AddWithValue("@FieldValue", dr("DocumentTitle"))
        insertCommand.ExecuteNonQuery()
    End Using
End While

我尝试重新启动 SQL Server 以清除所有锁定、重新编译存储过程、增加 SqlCommand 和 SqlConnection 超时,但均无济于事。

我检查了添加到参数中的数据,它是有效数据...如果我使用相同的数据手动调用存储过程,它可以正常工作。

我最初没有使用 Using 块,但更改了它以查看是否有一些资源问题没有得到处理/关闭。该实用程序的内存使用量徘徊在 5MB 左右,因此似乎没有任何内存问题。

有没有人对下一步尝试解决方案有什么建议?

编辑为每个评论请求添加循环和初始化代码

EDIT我更新了统计信息并重建了表索引,没有任何变化。

EDIT 数据被复制到的表上有三个索引(dmDocumentField)。如果我禁用所有三个索引,则存储过程会完美执行,尽管比索引存在时慢得多。如果我启用其中任何一个,那么该实用程序最多可以通过几百条记录,然后在 sproc 上以相同的超时终止。删除并重新创建索引无效。表结构和索引如下:

CREATE TABLE [dbo].[dmDocumentField](
[FieldKey] [bigint] IDENTITY(1,1) NOT NULL,
[DocumentKey] [char](36) NOT NULL,
[FieldId] [varchar](10) NOT NULL,
[FieldValue] [varchar](255) NOT NULL,
CONSTRAINT [PK_dmDocumentField] PRIMARY KEY NONCLUSTERED 
(
[FieldKey] ASC,
[DocumentKey] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

索引(除了 PK):

CREATE NONCLUSTERED INDEX [dmDocumentField_DocumentKey] ON [dbo].[dmDocumentField]
(
[DocumentKey] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

.

CREATE NONCLUSTERED INDEX [dmDocumentField_DocumentKey_IFieldId_IFieldValue] ON [dbo].[dmDocumentField]
(
[DocumentKey] ASC
)
INCLUDE (   [FieldId],
[FieldValue]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

【问题讨论】:

我还要提一下,该实用程序位于 VB.Net 4 中,而相关的 SQL Server 是 SQL Server 2008 R2 Standard。 SQL 分析器有什么异常吗? 您可以尝试使用Enterprise Library中的数据访问方法。 这段代码是在循环中调用的吗?如果是的话,你能不能也显示循环代码? 如果你使用 .AddWithValue ,那么 SQL Server 会为每个不同大小的参数生成一个不同的执行计划(根据我自己的测试)。如果您在创建 SQL 参数时设置了 .Size,那么您只会得到一个执行计划。 【参考方案1】:

我会试试这个:

... 
dr = readCommand.ExecuteReader() 
... 
insertCommand = New SqlCommand("dmDocumentFieldInsert", insertConn) 
insertCommand.CommandType = CommandType.StoredProcedure 
insertCommand.Parameters.AddWithValue("@DocumentKey", string.Empty)) 
insertCommand.Parameters.AddWithValue("@FieldId", "TITLE") 
insertCommand.Parameters.AddWithValue("@FieldValue", string.Empty)) 

Dim tr As SqlTransaction
tr = insertConn.BeginTransaction
insertCommand.Transaction = tr
While dr.Read() 
        insertCommand.Parameters("@DocumentKey").Value = dr("DocumentKey") 
        insertCommand.Parameters("@FieldValue").Value = dr("DocumentTitle")
        insertCommand.ExecuteNonQuery() 
End While 

tr.Commit()

这可能会占用更少的内存(创建命令、创建参数在循环内重复)并且有 150 万条记录可能会有所不同。注意我不知道真正的@DocumentKey 和 @FieldValue 的数据类型,假定为字符串,但如果不是这种情况,则使用适当的虚拟值更改初始设置。

【讨论】:

好点,我没有提到所有三个参数都是字符串。更具体地说,DocumentKey 是 char(36),FieldId 是 varchar(10),FieldValue 是 varchar(255)。 另外,我最初的代码非常接近您的建议,但在故障排除的热度中尝试了另一种方式。我只是将其改回您的建议,但结果没有改变。 :( This article 非常有趣。您的列是 VARCHAR 类型而不是 NVARCHAR 类型吗?在文章中,对从 UNICODE 转换产生的问题提出了强有力的建议。 其中两列是varchar,是的。从中提取值的原始列在数据类型中也匹配,因此我知道没有进行转换。 最后的想法,使用Transactions,看看它是否有什么不同【参考方案2】:

好吧,在与此作斗争之后,我手动进行了转换,因为这对某个特定客户来说是一次性问题。我仍然希望我知道为什么 SqlCommand 会超时,但 SSMS 没有,但此时它已经完成了。

【讨论】:

【参考方案3】:

您的命令是否对 DataReader 选择的同一个表执行操作?我有类似的情况,我正在做类似执行阅读器的事情

SELECT ID, Name FROM Table 1

然后我的超时命令正在遍历 DR 记录并执行类似

的操作
UPDATE Table1 SET Name = 'foo' WHERE ID = 1

当我将 SELECT 更改为使用 DataTable 而不是 DataReader 进行存储时,它立即工作,就像在 SSMS 中一样。

【讨论】:

以上是关于调用 SQL Server 存储过程的 SqlCommand 超时的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SQL Server 中检查调用存储过程

sql server显示远程过程调用失败怎么办

在SQL Server2005中,下面调用存储过程的语句错误的是:

SQL Server 存储过程包含 SQL 或调用查看

VBS 调用SQL Server加密存储过程提示:对象关闭时 不允许操作

sql server远程调用失败怎么解决