Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Come creare a livello di codice una tabella collegata ODBC a una vista di SQL Server e renderla modificabile?

Non è perché è senza DSN, ma perché l'hai creato tramite VBA. Se colleghi la vista tramite la GUI di Access, ti viene richiesta la chiave primaria.

Ma tramite VBA, non conosce la chiave primaria, quindi la vista collegata non è aggiornabile. Con una tabella, Access ottiene automaticamente la chiave primaria tramite ODBC, quindi la tabella funziona.

Soluzione: imposta la chiave primaria dopo aver collegato la vista tramite VBA:

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

Se hai molte viste e le ricolleghi regolarmente (ad es. passando dal database di sviluppo al database di produzione), diventa impraticabile codificare i loro nomi e PK. Ho scritto una funzione per recuperare tutti gli indici delle chiavi primarie dalle viste collegate e ricrearli dopo il collegamento.
Se vuoi, posso recuperarlo.

Modifica:
Questo è quello che faccio:

' 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

Per collegare una tabella o visualizzare la prima volta , io uso questo (strTable è il nome della tabella/vista):

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

Per le tabelle, la chiave primaria (PK) viene determinata automaticamente. Per una vista, ottengo la finestra di dialogo di accesso per specificare la PK, come se colleghi la vista manualmente.
Le informazioni PK sono memorizzate nell'oggetto TableDef per la vista collegata, quindi non devo mai codificarle da nessuna parte .

Per memorizzare le informazioni PK per tutte le viste collegate, ho questa tabella (per semplicità è una tabella locale nel frontend di Access):

t_LinkedViewPK
    ViewName        Text(100)
    IndexFields     Text(255)

e questa funzione. Tutte le visualizzazioni (e solo Views) sono chiamati "v_*", quindi posso elencarli per nome.
In realtà non sono sicuro se puoi determinare da un oggetto TableDef se punta a una tabella 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

Quando apporto modifiche alla tabella o alle strutture di visualizzazione, o cambio il database di origine (questo viene fatto modificando l'output di ODBC_String() ), chiamo questa funzione:

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:
Invece della tabella t_LinkedViewPK , è possibile utilizzare un oggetto dizionario. Ma durante lo sviluppo di questo, è stato molto utile averlo come un vero tavolo.