Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Como criar programaticamente uma tabela vinculada ODBC para uma exibição do SQL Server e que seja editável?


Não é porque é menos DSN, mas porque você o criou via VBA. Se você vincular a visualização por meio da GUI do Access, ela solicitará a chave primária.

Mas via VBA, ele não conhece a chave primária, portanto, a exibição vinculada não é atualizável. Com uma tabela, o Access obtém a chave primária automaticamente via ODBC, para que a tabela funcione.

Solução: defina a chave primária após vincular a visualização via VBA:
S = "CREATE INDEX PrimaryKey ON MyViewName (MyPrimaryKeyField) WITH PRIMARY"
DB.Execute S

Se você tem muitas visualizações e as vincula novamente regularmente (por exemplo, passando do banco de dados dev para o de produção), torna-se impraticável codificar seus nomes e PKs. Eu escrevi uma função para recuperar todos os índices de chave primária de visualizações vinculadas e recriá-los após a vinculação.
Se você quiser, eu posso desenterrar.

Editar:
Isso é o que eu faço:
' 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

Para vincular uma tabela ou visualização na primeira vez , eu uso isso (strTable é o nome da tabela/exibição):
DoCmd.TransferDatabase acLink, "ODBC", ODBC_String(), acTable, strTable, strTable, False, True

Para tabelas, a chave primária (PK) é determinada automaticamente. Para uma visualização, recebo a janela de diálogo Access para especificar o PK, da mesma forma que se vinculasse a visualização manualmente.
As informações de PK são armazenadas no objeto TableDef para a visualização vinculada, portanto, nunca preciso codificá-la em nenhum lugar .

Para armazenar as informações de PK para todas as visualizações vinculadas, tenho esta tabela (é uma tabela local no frontend do Access para simplificar):
t_LinkedViewPK
    ViewName        Text(100)
    IndexFields     Text(255)

e esta função. Todas as visualizações (e somente Views) são chamadas de "v_*", então posso listá-las pelo nome.
Na verdade, não tenho certeza se você pode determinar a partir de um objeto TableDef se ele aponta para uma tabela ou visão.
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

Quando faço alterações nas estruturas de tabela ou visualização, ou altero o banco de dados de origem (isso é feito alterando a saída de ODBC_String() ), eu chamo esta função:
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

Observação:
Em vez da tabela t_LinkedViewPK , um objeto de dicionário pode ser usado. Mas ao desenvolver isso, foi muito útil tê-lo como uma tabela real.