Het is niet omdat het DSN-minder is, maar omdat je het via VBA hebt gemaakt. Als u de weergave koppelt via de Access GUI, wordt u om de primaire sleutel gevraagd.
Maar via VBA kent het de primaire sleutel niet, dus de gekoppelde weergave kan niet worden bijgewerkt. Bij een tabel krijgt Access de primaire sleutel automatisch via ODBC, dus de tabel werkt.
Oplossing: stel de primaire sleutel in na het koppelen van de weergave via VBA:
S = "CREATE INDEX PrimaryKey ON MyViewName (MyPrimaryKeyField) WITH PRIMARY"
DB.Execute S
Als je veel views hebt en ze regelmatig opnieuw koppelt (bijvoorbeeld van dev naar de productiedatabase), wordt het onpraktisch om hun namen en PK's hard te coderen. Ik heb een functie geschreven om alle primaire-sleutelindexen van gekoppelde weergaven op te halen en ze na het koppelen opnieuw te maken.
Als je wilt, kan ik het opgraven.
Bewerken:
Dit is wat ik doe:
' 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
Een tafel koppelen of de eerste keer bekijken , ik gebruik dit (strTable is de naam van de tabel/weergave):
DoCmd.TransferDatabase acLink, "ODBC", ODBC_String(), acTable, strTable, strTable, False, True
Voor tabellen wordt de primaire sleutel (PK) automatisch bepaald. Voor een weergave krijg ik het Access-dialoogvenster om de PK te specificeren, hetzelfde als wanneer ik de weergave handmatig koppel.
De PK-informatie wordt opgeslagen in het TableDef-object voor de gekoppelde weergave, dus ik hoef deze nooit ergens hard te coderen .
Om de PK-informatie voor alle gekoppelde weergaven op te slaan, heb ik deze tabel (het is een lokale tabel in de Access-frontend voor de eenvoud):
t_LinkedViewPK
ViewName Text(100)
IndexFields Text(255)
en deze functie. Alle weergaven (en alleen Views) worden "v_*" genoemd, dus ik kan ze bij naam noemen.
Ik weet eigenlijk niet zeker of je aan de hand van een TableDef-object kunt bepalen of het naar een tabel of view verwijst.
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
Wanneer ik wijzigingen aanbreng in tabel- of weergavestructuren, of de brondatabase wijzig (dit wordt gedaan door de uitvoer van ODBC_String()
te wijzigen ), noem ik deze functie:
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
Opmerking:
In plaats van de tabel t_LinkedViewPK
, kan een woordenboekobject worden gebruikt. Maar tijdens het ontwikkelen hiervan was het erg handig om het als een echte tafel te hebben.