Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Comment créer par programmation une table liée ODBC dans une vue SQL Server et la rendre modifiable?

Ce n'est pas parce qu'il est sans DSN, mais parce que vous l'avez créé via VBA. Si vous liez la vue via l'interface graphique Access, elle vous demande la clé primaire.

Mais via VBA, il ne connaît pas la clé primaire, donc la vue liée n'est pas modifiable. Avec une table, Access obtient automatiquement la clé primaire via ODBC, donc la table fonctionne.

Solution : définir la clé primaire après avoir lié la vue via VBA :

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

Si vous avez de nombreuses vues et que vous les reliez régulièrement (par exemple, en passant de la base de données de développement à la base de données de production), il devient impossible de coder en dur leurs noms et leurs PK. J'ai écrit une fonction pour récupérer tous les index de clé primaire à partir de vues liées et les recréer après la liaison.
Si vous le souhaitez, je peux le déterrer.

Modifier :
Voici ce que je fais :

' 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

Pour lier un tableau ou afficher la première fois , j'utilise ceci (strTable est le nom de la table/vue) :

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

Pour les tables, la clé primaire (PK) est déterminée automatiquement. Pour une vue, j'obtiens la fenêtre de dialogue Access pour spécifier le PK, comme si je liais la vue manuellement.
Les informations PK sont stockées dans l'objet TableDef pour la vue liée, donc je n'ai jamais à la coder en dur n'importe où .

Pour stocker les informations PK pour toutes les vues liées, j'ai cette table (c'est une table locale dans l'interface Access pour plus de simplicité):

t_LinkedViewPK
    ViewName        Text(100)
    IndexFields     Text(255)

et cette fonction. Toutes les vues (et uniquement Views) s'appellent "v_*", donc je peux les lister par nom.
Je ne sais pas si vous pouvez déterminer à partir d'un objet TableDef s'il pointe vers une table ou une vue.

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

Lorsque j'apporte des modifications aux structures de table ou de vue, ou que je modifie la base de données source (cela se fait en modifiant la sortie de ODBC_String() ), j'appelle cette fonction :

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

Remarque :
Au lieu de la table t_LinkedViewPK , un objet dictionnaire peut être utilisé. Mais lors du développement, il était très utile de l'avoir sous forme de tableau réel.