Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Wie erstelle ich programmgesteuert eine ODBC-verknüpfte Tabelle mit einer SQL Server-Ansicht und bearbeite sie?

Das liegt nicht daran, dass es DSN-los ist, sondern daran, dass Sie es über VBA erstellt haben. Wenn Sie die Ansicht über die Access-GUI verknüpfen, werden Sie nach dem Primärschlüssel gefragt.

Aber über VBA kennt es den Primärschlüssel nicht, daher ist die verknüpfte Ansicht nicht aktualisierbar. Bei einer Tabelle erhält Access den Primärschlüssel automatisch über ODBC, sodass die Tabelle funktioniert.

Lösung: Legen Sie den Primärschlüssel fest, nachdem Sie die Ansicht über VBA verknüpft haben:

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

Wenn Sie viele Ansichten haben und diese regelmäßig neu verknüpfen (z. B. von der Entwicklungs- zur Produktionsdatenbank), wird es unpraktisch, ihre Namen und PKs fest zu codieren. Ich habe eine Funktion geschrieben, um alle Primärschlüsselindizes aus verknüpften Ansichten abzurufen und sie nach dem Verknüpfen neu zu erstellen.
Wenn Sie möchten, kann ich sie ausgraben.

Bearbeiten:
Das mache ich:

' 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

Um eine Tabelle das erste Mal zu verknüpfen oder anzuzeigen , verwende ich dies (strTable ist der Name der Tabelle/Ansicht):

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

Bei Tabellen wird der Primärschlüssel (PK) automatisch ermittelt. Für eine Ansicht erhalte ich das Access-Dialogfenster, um den PK anzugeben, genauso wie wenn ich die Ansicht manuell verknüpfe.
Die PK-Informationen werden im TableDef-Objekt für die verknüpfte Ansicht gespeichert, sodass ich sie nie irgendwo hartcodieren muss .

Um die PK-Informationen für alle verknüpften Ansichten zu speichern, habe ich diese Tabelle (der Einfachheit halber ist es eine lokale Tabelle im Access-Frontend):

t_LinkedViewPK
    ViewName        Text(100)
    IndexFields     Text(255)

und diese Funktion. Alle Ansichten (und nur Views) heißen "v_*", also kann ich sie nach Namen auflisten.
Ich bin mir eigentlich nicht sicher, ob man anhand eines TableDef-Objekts feststellen kann, ob es auf eine Tabelle oder View zeigt.

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

Wenn ich Änderungen an Tabellen- oder Ansichtsstrukturen vornehme oder die Quelldatenbank ändere (dies geschieht durch Ändern der Ausgabe von ODBC_String() ), nenne ich diese Funktion:

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

Hinweis:
Anstelle der Tabelle t_LinkedViewPK , könnte ein Dictionary-Objekt verwendet werden. Aber während der Entwicklung war es sehr nützlich, es als echten Tisch zu haben.