【问题标题】:How to programmatically create an ODBC Linked Table to a SQL Server View and have it be editable?如何以编程方式将 ODBC 链接表创建到 SQL Server 视图并使其可编辑?
【发布时间】:2015-11-25 19:23:40
【问题描述】:

当我使用向导创建到 SQL Server 的 DSN 连接时,我可以将它链接到视图。在这种情况下,Access 会将其识别为可编辑的表格。

但是,如果我使用 vba 代码(来自https://support.microsoft.com/en-us/kb/892490 的方法 1)使用无 DSN 连接到视图,它会被链接为不可更新的表。

我不知道为什么会有差异,但我如何才能连接到 SQL Server 中的视图(作为表或 Access 中的查询)并使其可更新?

编辑:当我使用 SQL Server 中的表而不是视图进行无 DSN 连接时,它可以在 Access 中更新。我猜我的问题与没有唯一 ID 的视图有关,但我很困惑为什么 DSN 连接可以更新,而 DSN-less 则不能。

【问题讨论】:

    标签: sql-server vba ms-access dsn


    【解决方案1】:

    这不是因为它没有 DSN,而是因为您是通过 VBA 创建的。如果您通过 Access GUI 链接视图,它会要求您提供主键。

    但是通过 VBA,它不知道主键,所以链接视图是不可更新的。对于表,Access 会通过 ODBC 自动获取主键,因此表可以正常工作。

    解决方法:通过VBA链接视图后设置主键:

    S = "CREATE INDEX PrimaryKey ON MyViewName (MyPrimaryKeyField) WITH PRIMARY"
    DB.Execute S
    

    如果您有很多视图,并定期重新链接它们(例如从开发数据库转到生产数据库),硬编码它们的名称和 PK 将变得不切实际。我编写了一个函数来从链接视图中检索所有主键索引,并在链接后重新创建它们。
    如果你愿意,我可以把它挖出来。


    编辑:
    我就是这样做的:

    ' This function returns the full DSN-less connect string
    Private Function ODBC_String() As String
        ' In the real world there are several constants and variable in there
        ODBC_String = "ODBC;DRIVER={SQL Server};SERVER=aaa;DATABASE=bbb;UID=ccc;PWD=ccc;LANGUAGE=us_english;TRUSTED_CONNECTION=No"
    End Function
    

    第一次链接表或视图,我使用这个(strTable 是表/视图名称):

    DoCmd.TransferDatabase acLink, "ODBC", ODBC_String(), acTable, strTable, strTable, False, True
    

    对于表,主键 (PK) 是自动确定的。对于视图,我会在访问对话框窗口中指定 PK,就像我手动链接视图一样。
    PK 信息存储在链接视图的 TableDef 对象中,因此我无需在任何地方对其进行硬编码。

    为了存储所有链接视图的 PK 信息,我有这个表(为简单起见,它是 Access 前端中的本地表):

    t_LinkedViewPK
        ViewName        Text(100)
        IndexFields     Text(255)
    

    还有这个功能。所有视图(视图)都称为“v_*”,因此我可以按名称列出它们。
    我实际上不确定您是否可以从 TableDef 对象中确定它是指向表还是视图。

    Private Sub StoreViewPKs()
    
        Dim TD As TableDef
        Dim idx As index
        Dim FD As Field
        Dim RS As Recordset
        Dim S As String
    
        ' DB is a global Database object, set to CurrentDB
        DB.Execute "Delete * From t_LinkedViewPK"
        Set RS = DB.OpenRecordset("t_LinkedViewPK")
    
        For Each TD In DB.TableDefs
            If TD.Name Like "v_*" Then
                ' Views must have exactly one index. If not: panic!
                If TD.Indexes.Count <> 1 Then
                    MsgBox "View " & TD.Name & " has " & TD.Indexes.Count & " Indizes.", vbCritical
                    Stop
                End If
    
                Set idx = TD.Indexes(0)
                ' Build field list (the index may contain multiple fields)
                S = ""
                For Each FD In idx.Fields
                    If S <> "" Then S = S & ", "
                    S = S & FD.Name
                Next FD
    
                RS.AddNew
                RS!ViewName = TD.Name
                RS!IndexFields = S
                RS.Update
            End If
        Next TD
    
        RS.Close
    
    End Sub
    

    当我对表或视图结构进行更改,或更改源数据库(这是通过更改ODBC_String() 的输出来完成的)时,我调用此函数:

    Public Function Sql_RefreshTables()
    
        Dim TD As TableDef
        Dim S As String
        Dim IdxFlds As String
    
        DB.TableDefs.Refresh
    
        ' save current Indizes for Views (recreated after .RefreshLink)
        Call StoreViewPKs
    
        For Each TD In DB.TableDefs
            If Len(TD.Connect) > 0 Then
                If Left(TD.Connect, 5) = "ODBC;" Then
    
                    Debug.Print "Updating " & TD.Name
                    TD.Connect = ODBC_String()
                    TD.RefreshLink
    
                    ' View?
                    If TD.Name Like "v_*" Then
                        IdxFlds = Nz(DLookup("IndexFields", "t_LinkedViewPK", "ViewName = '" & TD.Name & "'"))
                        If IdxFlds = "" Then Stop
    
                        ' Create PK
                        S = "CREATE INDEX PrimaryKey ON " & TD.Name & " (" & IdxFlds & ") WITH PRIMARY"
                        DB.Execute S
                    End If
    
                End If
            End If
        Next TD
    
        DB.TableDefs.Refresh
    
    End Function
    

    注意:
    可以使用字典对象代替表t_LinkedViewPK。但是在开发时,将它作为一个实际的表格非常有用。

    【讨论】:

    • 啊!当然,如果不是太麻烦的话。或者你可以给我我需要的功能,如果需要更少的时间,我可以自己弄清楚逻辑......
    【解决方案2】:

    安德烈的回答是正确的。我正在使用更复杂的代码来创建索引——这只是表面上的改变:

    Public Function RefreshIndexes()
        On Error Resume Next
        CurrentDb.Execute "CREATE UNIQUE INDEX [__uniqueindex] ON MyViewName (MyPrimaryKeyField) WITH PRIMARY;", dbFailOnError
        CurrentDb.Execute "CREATE UNIQUE INDEX [__uniqueindex] ON MyViewName2 (MyPrimaryKeyField2) WITH PRIMARY;", dbFailOnError
    End Function
    

    我在用户打开 Access 时调用此命令,并且仅用于没有索引的视图。

    【讨论】:

    • 主键 (WITH PRIMARY) 在定义上是唯一的,所以这有点多余。
    • 酷,我不知道。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-19
    • 2014-04-10
    • 2013-11-04
    • 1970-01-01
    • 1970-01-01
    • 2023-03-25
    相关资源
    最近更新 更多