MS Access VBA 和 SQL Server - 记录集更新时 ODBC 调用失败

Posted

技术标签:

【中文标题】MS Access VBA 和 SQL Server - 记录集更新时 ODBC 调用失败【英文标题】:MS Access VBA & SQL Server - ODBC Call Failed on .Update of Recordset 【发布时间】:2021-07-21 13:23:52 【问题描述】:

我有一个 MS Access 应用程序,我正在使用 SQL Server Management Studio (SSMS) 将它转换为 SQL Server 后端(仍然是 Access FE)。

该应用程序存储人力资源信息,并且有一个功能应该是根据雇佣日期和试用期来建立试用计划到期日期。

函数如下:

Private Sub BtnBuildSked_Click()
    Const SUB_NAME As String = "BtnBuildSked_Click"
    On Error GoTo ErrCond
    Dim ThisReport, myresponse, MyStyle As Integer
    Dim MYDB As Database
    Dim Myrst, PdRec As DAO.Recordset
    'Dim Myfrm As Form
    Dim PdStr As String
    Dim s, strsql As String
    Dim t As TTracking
    
    If Me.PaysrID.Value = "ON LEAVE" Then
        MsgBox ("Record is marked ON LEAVE. Cannot proceed.")
        Exit Sub
    End If
        
    'Set Myfrm = Screen.ActiveForm
    Set MYDB = CurrentDb
    s = "SELECT * FROM tblProbationReports"
    Set Myrst = MYDB.OpenRecordset(s, dbOpenDynaset, dbSeeChanges)
    MyStyle = vbOKOnly + vbExclamation
    
    If IsNull(Me.PaysrID) Then
        MsgBox ("Cannot process for empty Employee ID")
        Exit Sub
    End If
    
    If (IsNull(Me.ApptDate)) Then
        myresponse = MsgBox("Appointment Date field is empty ", vbCritical)
        Exit Sub
    End If
    
    '----- Check that Probation Term ID is not blank
    If (Me.ProbationTermId.Value < 1 Or Me.ProbationTermId.Value > 7 Or IsNull(Me.ProbationTermId)) Then
        myresponse = MsgBox("You must select a probationary period to be able to build a schedule", MyStyle)
        Exit Sub
    End If
    
    '----- Warning that any existing records for this employee will be deleted
    myresponse = MsgBox("Warning!! This will delete any current probation schedule records for this Employee and Line No. combination. Do you want to proceed  ?", vbYesNo)
    Debug.Print myresponse
    If myresponse = 7 Then
        Exit Sub
    End If
    
    '------ Delete previous Probationary records for employee ; Keys = SS# and Line#
    strsql = "DELETE tblProbationReports.* " & _
             "FROM tblProbationReports " & _
             "WHERE tblProbationReports.LineNo= '" & Me.LineNo & "' and tblProbationReports.PaysrID = '" & Me.PaysrID & "' "
            
    DoCmd.SetWarnings False
    DoCmd.RunSQL (strsql)
    DoCmd.SetWarnings True
 
    '----- Point to the approp recoird in Probation Data
    PdStr = "SELECT * " & _
            "FROM [tblProbationData] " & _
            "WHERE [ProbationTermID] = " & Me.ProbationTermId.Value
            
    'Set PdRec = MYDB.OpenRecordset("SELECT * FROM [tblProbationData] WHERE [ProbationTermID] = & me.ProbationTermID.Value & ")
    Set PdRec = MYDB.OpenRecordset(PdStr)
    If Not PdRec.EOF Then
        For ThisReport = 1 To PdRec("ProbationReports")
        
        'With Myrst
            Myrst.AddNew
            Myrst.Fields("LineNumber") = Me.LineNo
            Myrst.Fields("SSNO") = Me.SSNo
            Myrst.Fields("PaysrID") = Me.PaysrID
            Myrst.Fields("ReportNo") = ThisReport
            Select Case ThisReport
                Case 1
                    Myrst.Fields("FromDate") = Me.ApptDate
                    Myrst.Fields("ToDate") = Me.ApptDate + (PdRec("Report1weeks") * 7) '-1
                    Myrst.Fields("DueDate") = Me.ApptDate + (PdRec("Report1weeks") * 7) - 1 - 14
                    Myrst.Fields("SentDate") = Me.ApptDate + (PdRec("Report1weeks") * 7) - 1 - 28
                Case 2
                    Myrst.Fields("FromDate") = Me.ApptDate + (PdRec("Report1weeks") * 7)
                    Myrst.Fields("ToDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks")) * 7) '- 1
                    Myrst.Fields("DueDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks")) * 7) - 1 - 14
                    Myrst.Fields("SentDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks")) * 7) - 1 - 28
                Case 3
                    Myrst.Fields("FromDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks")) * 7)
                    Myrst.Fields("ToDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks")) * 7) '- 1
                    Myrst.Fields("DueDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks")) * 7) - 1 - 14
                    Myrst.Fields("SentDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks")) * 7) - 1 - 28
                Case 4
                    Myrst.Fields("FromDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks")) * 7)
                    Myrst.Fields("ToDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks") + PdRec("Report4weeks")) * 7) '- 1
                    Myrst.Fields("DueDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks") + PdRec("Report4weeks")) * 7) - 1 - 14
                    Myrst.Fields("SentDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks") + PdRec("Report4weeks")) * 7) - 1 - 28
                Case 5
                    Myrst.Fields("FromDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks") + PdRec("Report4weeks")) * 7)
                    Myrst.Fields("ToDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks") + PdRec("Report4weeks") + PdRec("Report5weeks")) * 7) '- 1
                    Myrst.Fields("DueDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks") + PdRec("Report4weeks") + PdRec("Report5weeks")) * 7) - 1 - 14
                    Myrst.Fields("SentDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks") + PdRec("Report4weeks") + PdRec("Report5weeks")) * 7) - 1 - 28
                    Myrst.Fields("SentDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks") + PdRec("Report4weeks") + PdRec("Report5weeks")) * 7) - 1 - 42
                    Myrst.Fields("SentDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks") + PdRec("Report4weeks") + PdRec("Report5weeks")) * 7) - 1 - 56
                Case 6
                    Myrst.Fields("FromDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks") + PdRec("Report4weeks") + PdRec("Report5weeks")) * 7)
                    Myrst.Fields("ToDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks") + PdRec("Report4weeks") + PdRec("Report5weeks") + PdRec("Report6weeks")) * 7) '- 1
                    Myrst.Fields("DueDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks") + PdRec("Report4weeks") + PdRec("Report5weeks") + PdRec("Report6weeks")) * 7) - 1 - 14
                    Myrst.Fields("SentDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks") + PdRec("Report4weeks") + PdRec("Report5weeks") + PdRec("Report6weeks")) * 7) - 1 - 28
                    Myrst.Fields("SentDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks") + PdRec("Report4weeks") + PdRec("Report5weeks") + PdRec("Report6weeks")) * 7) - 1 - 42
                    Myrst.Fields("SentDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks") + PdRec("Report4weeks") + PdRec("Report5weeks") + PdRec("Report6weeks")) * 7) - 1 - 56
                Case 7
                    Myrst.Fields("FromDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks") + PdRec("Report4weeks") + PdRec("Report5weeks")) * 7)
                    Myrst.Fields("ToDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks") + PdRec("Report4weeks") + PdRec("Report5weeks") + PdRec("Report6weeks")) * 7) '- 1
                    Myrst.Fields("DueDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks") + PdRec("Report4weeks") + PdRec("Report5weeks") + PdRec("Report6weeks")) * 7) - 1 - 14
                    Myrst.Fields("SentDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks") + PdRec("Report4weeks") + PdRec("Report5weeks") + PdRec("Report6weeks")) * 7) - 1 - 28
                    Myrst.Fields("SentDate") = Me.ApptDate + ((PdRec("Report1weeks") + PdRec("Report2weeks") + PdRec("Report3weeks") + PdRec("Report4weeks") + PdRec("Report5weeks") + PdRec("Report6weeks")) * 7) - 1 - 56
            End Select
            Myrst.Fields("Received") = False
            Myrst.Update
        
            
        'End With
        Next ThisReport
        
     End If
      
        'set up tracking manually
        t.Comment = "Build New Schedule"
        t.FieldName = "LineNo"
        t.ItemID = Me.LineNo
        t.NewValue = "New Schedule Built"
        t.OldValue = ""
        t.TableName = "tblProbationReports"
        t.TrackingType = TR_TYPE_NEW
        SaveTrans t
        
     Myrst.Close
     PdRec.Close
    
'Myfrm.SetFocus
Me.Recalc
MsgBox ("Probation dates created")



Exit Sub
ErrCond:
    EventLogging AppSession.UserName, MSG_TYPE_ERROR, Err.Number, Err.Description, MOD_NAME & "." & SUB_NAME, AppSession.AppSilent
End Sub

前几个块是检查格式正确的员工 ID 和活动状态。

代码警告任何新的计划都会删除以前的值。

那么应用程序应该根据试用期(各种案例陈述)和就业日期来构建新的时间表。

但是当代码下降到 .Update 时,它​​会失败并显示“ODBC 调用失败”。

以下是迄今为止的注意事项和故障排除摘要:

与 BE 的连接没有问题。我能够毫无问题地创建、更新和删除员工记录。通过 Access FE 打开时,我可以看到表格中的数据。我可以生成报告。

“试用报告”表没有主键,我需要添加主键才能在 SSMS 中创建审核触发器(我知道表应该有主键,但我不是最初的开发者)。

我在“Probation Reports”表中创建了“RecordID”主键并将其设为“Identity”,以便自动增加唯一标识符。

在 SSMS 中使 RecordID“身份”按顺序递增时,Access 在 Recordset 上出错。“试用报告”的打开...“具有身份的表在打开 Recordset 时需要 dbSeeChanges”

尝试 Recordset.Open ("name", ,dbSeeChanges) 仍然出错,“具有标识的表在打开 Recordset 时需要 dbSeeChanges”。 Access 未读取“dbSeeChanges”选项值

我需要添加一个打开的“类型”(尽管是可选的),以便 Access 读取“dbSeeChanges”的打开的“选项”值。

我已经尝试了 Open "Type" docs.microsoft.com/en-us/office/client-developer/access/desktop-database-reference/database-openrecordset-method-dao 的每个值(甚至那些没有没有意义)。这是 .Update 因“ODBC 连接失败”而中断的地方

我尝试添加“dbOpenOptimistic”的 LockEdit 值选项,但仍然收到“ODBC 调用失败”错误...

我不知道从这里去哪里......


更新 - 根据 cmets 中的反馈,我尝试直接向表中添加值并收到以下错误(截图)

【问题讨论】:

要做的第一件事:尝试直接在链接表 tblProbationData 中编辑 + 插入(在数据表中)。您可能会在那里收到更好的错误消息。 @Andre - 我现在就试试。 2 分钟。 @Andre - 这绝对是一个不同的错误!我将用这个和屏幕截图更新问题。关于二进制截断... 在 VBA 中,您需要拥有 Dim 语句中每个变量的数据类型。 Dim Myrst, PdRec As DAO.Recordset => Myrst 将是 Variant,而不是 Recordset。在这里应该没关系,但仍然如此。 @Andre - 我会和 Myrst 一起记住这一点。不过,我认为这不是问题所在,因为这在 MS Access BE 上运行良好。除非这是我必须做出的改变,因为这将如何转化为 SSMS(?) 【参考方案1】:

定义记录集(无论是在访问中,还是在使用 sql server 时)。

Dim rst      AS DAO.Reocdset

您可以省略 DAO。但你不应该。

对于任何链接的 sql server 表?

该表必须定义一个 PK。如果您在设计模式下打开访问链接表(忽略仅就绪警告),请检查两件事:

首先,即使不是 200%,也要 100% 确定您看到定义的 PK。

第二:此时,检查日期时间列的数据类型。如果它们是代替日期时间的文本,您必须立即停止并解决该问题。

如果日期时间列被视为文本,那么您有两种选择:

将 sql server 列数据类型从 datetime2(新的默认值)更改为 datetime,然后重新链接表。现在根据 aobve 再次检查设计中的表 - 检查列数据类型。

您还可以考虑安装更新的 ODBC Native 11 或更高版本的驱动程序 - 它们支持 datetime2 - 内置的旧 ODBC 驱动程序不支持!!! - 它将这些列视为文本。但是,缺点是您必须在每个工作站上安装本机 11(或更高版本的驱动程序 - 现在为 17 版)。因此,由您决定哪个更省事(在每个工作站上安装本机驱动程序,或者将 sql server 列时间翻转回 datetime 并且不使用 datetime2)。

下一期。

访问打开一个表:

Dim  rst   AS DAO.RecordSet

set rst = currentdb.OpenReocrdSet(" table name or sql goes here")

访问打开一个链接表到sql server。

   Dim rst             As DAO.Recordset
   Dim strSQL          As String
   
   strSQL = "SELECT * FROM tblInvoice where InvoiceNum = " & InvoiceNumber
   
   Set rst = CurrentDb.OpenRecordset(strSQL, dbOpenDynaset, dbSeeChanges)

所以,你总是包含 dbOpenDynaset、dbSeeChanges

如果你刚刚做了一个访问 sql server 的迁移?然后我在一个相对较大的应用程序中找到 EVEN,使用全局搜索和替换,(在我的粘贴缓冲区中使用 ,dbOpenDynaset,dbSeeChanges?它不应该花费更多的时间然后说大约 5 分钟。所以去更改现在运行的所有 VBA reocrdset 代码在链接表上。

因此,您必须像这样在打开的记录集上添加/拥有/包含两个额外的参数:

Set rst = CurrentDb.OpenRecordset(strSQL, dbOpenDynaset, dbSeeChanges)

还有一些得到你的

在 sql server 表设计中,所有位字段都必须为 false 设置默认值(例如 0)。

如果您收到可怕的“其他人已更新”记录错误?然后您需要在该表中添加一个时间戳列(不要将时间戳与日期时间混淆 - 时间戳是我们所说的行版本列 - 与日期时间无关,您永远不会触摸或编辑或更改该行版本列。

【讨论】:

抱歉耽搁了。 UpVoted 因为您的回复有很多非常有用/有用的提示。而且,如果其他人像我们一样经历这种转变,我认为他们会很高兴不必像我们一样艰难地学习这一点(并且在某些方面仍然必须这样做)。谢谢!【参考方案2】:

罪魁祸首是/实际上是这个数据库中的字段之一(以及上面的后续代码):“LineNo”

“LineNo”是 SQL Server 中的保留字。

在实施了各种建议之后,这个从......我什至不记得了,但这里有一篇 SQL Server 中的保留字供其他人参考:

https://docs.microsoft.com/en-us/sql/t-sql/language-elements/reserved-keywords-transact-sql?view=sql-server-ver15

将此字段的所有引用从“LineNo”更改为“LineNum”解决了所有错误。

【讨论】:

以上是关于MS Access VBA 和 SQL Server - 记录集更新时 ODBC 调用失败的主要内容,如果未能解决你的问题,请参考以下文章

VBA 代码中的 MS Access SQL 查询

MS Access - 在 VBA 中编写 SQL

MS-Access 获取字段值,通过 VBA 函数运行,并发送到 SQL

MS Access VBA 和 SQL Server - 记录集更新时 ODBC 调用失败

MS Access VBA 中的 SQL 指令

从 MS Access VBA 前端在 SQL 中使用 SESSION_CONTEXT