【问题标题】:Query Timeout Expired when updating SQL Server from VB,NET从 VB,NET 更新 SQL Server 时查询超时
【发布时间】:2020-02-13 16:58:32
【问题描述】:

我正在从 VB.NET 更新 SQL Server 并不断收到“查询超时错误”,我有很多按顺序运行的子例程,如下所示:

Public Shared Sub Update_DailyRatings()
    Dim stallStats As String = ""
    Dim win As Integer = 0
    Dim mSplit As Array

    Dim cn As OleDbConnection = New OleDbConnection(MainForm.connectStringPublic)
    cn.Open()

    Dim selectString As String = "Select * FROM DailyRatings"

    Dim cmd As OleDbCommand = New OleDbCommand(selectString, cn)
    Dim reader As OleDbDataReader = cmd.ExecuteReader()

    While (reader.Read())
        stallStats = Get_Stall_Stats(reader("Track").ToString, CInt(reader("Stall")), CDbl(reader("Distance")))
        If stallStats = "" Then
            MainForm.NonQuery("UPDATE DailyRatings SET StallWin = 999 WHERE Horse = '" & reader("Horse").ToString & "'")
        Else
            mSplit = Split(stallStats, ",")
            win = mSplit(0)

            MainForm.NonQuery("UPDATE DailyRatings SET StallWin = " & win & " WHERE Horse = '" & reader("Horse").ToString & "'")
        End If
    End While

    reader.Close()
    cn.Close()
End Sub

NonQuery sub 看起来像这样:

    Public Sub NonQuery(ByVal SQL As String)

    Dim query As String = SQL

    Try

        Dim cn3 As OleDbConnection = New OleDbConnection(connectStringPublic)

        cn3.Open()

        Dim cmd As OleDbCommand = New OleDbCommand(query, cn3)
        cmd.CommandTimeout = 90
        cmd.ExecuteNonQuery()
        cn3.Close()
        cn3.Dispose()
        cmd.Dispose()
        OleDbConnection.ReleaseObjectPool()
    Catch e As System.Exception
        Clipboard.SetText(query)
        MsgBox(e.Message)
    Finally

    End Try
End Sub

如您所见,我一直在尝试解决这个问题,这是我在其他线程中发现的,例如延长超时时间和使用 Dispose() 和 ReleaseObjectPool() 方法,但它没有奏效,我仍然得到查询超时按顺序运行我的所有潜艇时至少出错一次,它也不总是同一个潜艇。

我最近从 Access 迁移,这在 Access 中从未发生过。

【问题讨论】:

  • 您的 OleDbDataReader 可能会在 'Read` 方法中锁定您的表,从而导致死锁。
  • 我怎样才能阻止这种情况发生?
  • 您可以使用OleDbDataAdapter 将数据填充到DataTable 中,而不是使用OleDbDataReader,然后遍历行以运行更新。

标签: sql-server vb.net


【解决方案1】:

如果您正在处理 Sql Server,为什么要使用 OleDb?我猜那是真正的访问。

当您的 DataReader 打开时,您的连接保持打开状态。随着您正在进行的处理量,难怪您的连接超时。

首先,连接和其他几个数据库对象不仅需要关闭,还需要释放。它们可能包含在 .Dispose 方法中释放的非托管资源。如果您使用的是公开 .Dispose 方法的对象,请使用 Using...End Using 块。即使出现错误,这也会解决这个问题。

实际上,您有 2 个不同的操作正在进行。首先检索 DailyRatings,然后根据检索到的数据更新 DailyRatings。所以我们用第一块数据填充数据表并将其传递给第二个操作。我们的第一个连接已关闭并被释放。

在操作 2 中,我们像以前一样创建连接和命令对象,只是现在我们的命令有参数。每个 .Execute 的命令模式都是相同的,只是参数的值发生了变化。这种模式允许数据库(至少在 Sql Sever 中)缓存查询计划并提高性能。

Public Shared Function GetDailyRatings() As DataTable
    Dim dt As New DataTable
    Using cn As New OleDbConnection(MainForm.connectStringPublic),
            cmd As New OleDbCommand("Select * FROM DailyRatings", cn)
        cn.Open()
        dt.Load(cmd.ExecuteReader)
    End Using
    Return dt
End Function

Public Sub UpdateDailyRatings()
    Dim dt = GetDailyRatings()
    Using cn As New OleDbConnection(connectStringPublic),
            cmd As New OleDbCommand("UPDATE DailyRatings SET StallWin = @Stall WHERE Horse = @Horse")
        cmd.Parameters.Add("@Stall", OleDbType.Integer)
        cmd.Parameters.Add("@Horse", OleDbType.VarChar)
        cn.Open()
        For Each row As DataRow In dt.Rows
            cmd.Parameters("@Horse").Value = row("Horse").ToString
            Dim stallStats As String = Get_Stall_Stats(row("Track").ToString, CInt(row("Stall")), CDbl(row("Distance")))
            If stallStats = "" Then
                cmd.Parameters("@Stall").Value = 999
            Else
                cmd.Parameters("@Stall").Value = CInt(stallStats.Split(","c)(0))
            End If
            cmd.ExecuteNonQuery()
        Next
    End Using
End Sub

Private Function GetStallStats(Track As String, Stall As Integer, Distance As Double) As String
    Dim s As String
    'Your code here
    Return s
End Function

注意:OleDb 不注意参数名称。它们在查询语句中出现的顺序必须与它们添加到参数集合中的顺序相匹配。

【讨论】:

  • 是的,因为 Access,我使用的是 Oledb。实际上,我将通过将所有更新查询添加到字符串列表并在第一个连接关闭后执行它们来解决问题,因为要更新的代码太多,但是您的回答解释了很多,谢谢。
  • 其实我猜这取决于GetStallStats中有多少代码。构建字符串应该可以,但请记住每个字符串都必须独立执行。
【解决方案2】:

OleDbDataReader 可能会锁定您的表或连接,因为它正在使用繁忙的连接获取数据。您可以使用OleDbDataAdapter 将数据存储在DataTable 中并循环通过它来运行更新。下面是 sn-p 你的代码的样子:

Dim cmd As OleDbCommand = New OleDbCommand(selectString, cn)
Dim adapter As OleDbDataAdapter = New OleDbDataAdapter(cmd)
Dim dt As New DataTable()
adapter.Fill(dt)

For Each reader As DataRow In dt.Rows
    stallStats = Get_Stall_Stats(reader("Track").ToString, CInt(reader("Stall")), CDbl(reader("Distance")))
    If stallStats = "" Then
        MainForm.NonQuery("UPDATE DailyRatings SET StallWin = 999 WHERE Horse = '" & reader("Horse").ToString & "'")
    Else
        mSplit = Split(stallStats, ",")
        win = mSplit(0)

        MainForm.NonQuery("UPDATE DailyRatings SET StallWin = " & win & " WHERE Horse = '" & reader("Horse").ToString & "'")
    End If
Next
cn.Close()

【讨论】:

  • 好的,干杯,我需要更改很多代码,但我会试一试。
  • 不,不是。用适配器替换 reader 只需 4-5 行,将更改的循环将保持不变。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-24
  • 2012-02-29
相关资源
最近更新 更多