sql >> Base de Datos >  >> RDS >> Sqlserver

¿Cómo crear mediante programación una tabla vinculada ODBC a una vista de SQL Server y hacer que sea editable?

No es porque no tenga DSN, sino porque lo creaste a través de VBA. Si vincula la vista a través de la GUI de Access, le pedirá la clave principal.

Pero a través de VBA, no conoce la clave principal, por lo que la vista vinculada no se puede actualizar. Con una tabla, Access obtiene la clave principal automáticamente a través de ODBC, por lo que la tabla funciona.

Solución: establezca la clave principal después de vincular la vista a través de VBA:

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

Si tiene muchas vistas y las vuelve a vincular regularmente (por ejemplo, al pasar de la base de datos de desarrollo a la de producción), no resulta práctico codificar sus nombres y PK. Escribí una función para recuperar todos los índices de clave principal de las vistas vinculadas y volver a crearlos después de la vinculación.
Si quieres, puedo desenterrarla.

Editar:
Esto es lo que hago:

' 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 una tabla o ver la primera vez , uso esto (strTable es el nombre de la tabla/vista):

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

Para las tablas, la clave principal (PK) se determina automáticamente. Para una vista, obtengo la ventana de diálogo de Acceso para especificar el PK, igual que si vinculara la vista manualmente.
La información de PK se almacena en el objeto TableDef para la vista vinculada, por lo que nunca tengo que codificarla en ningún lugar .

Para almacenar la información de PK para todas las vistas vinculadas, tengo esta tabla (es una tabla local en la interfaz de Access para simplificar):

t_LinkedViewPK
    ViewName        Text(100)
    IndexFields     Text(255)

y esta función. Todas las vistas (y solo Views) se llaman "v_*", por lo que puedo enumerarlos por nombre.
En realidad, no estoy seguro de si puede determinar a partir de un objeto TableDef si apunta a una tabla o a una vista.

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

Cuando realizo cambios en las estructuras de tablas o vistas, o cambio la base de datos de origen (esto se hace cambiando la salida de ODBC_String() ), llamo a esta función:

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

Nota:
En lugar de la tabla t_LinkedViewPK , se podría utilizar un objeto de diccionario. Pero mientras desarrollaba esto, fue muy útil tenerlo como una tabla real.