调用 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 Server2005中,下面调用存储过程的语句错误的是: