Co robi Access, gdy użytkownik wprowadza zmiany w danych w tabeli połączonej ODBC?
Nasza seria śledzenia ODBC jest kontynuowana, aw tym czwartym artykule wyjaśnimy, jak wstawiać i aktualizować rekord danych w zestawie rekordów, a także proces usuwania rekordu. W poprzednim artykule dowiedzieliśmy się, jak program Access radzi sobie z wypełnianiem danych ze źródeł ODBC. Widzieliśmy, że typ zestawu rekordów ma istotny wpływ na sposób, w jaki program Access będzie formułował zapytania do źródła danych ODBC. Co ważniejsze, odkryliśmy, że w przypadku zestawu rekordów typu Dynaset program Access wykonuje dodatkową pracę, aby uzyskać wszystkie informacje wymagane do wybrania pojedynczego wiersza za pomocą klucza. Będzie to miało zastosowanie w tym artykule, w którym zbadamy, jak obsługiwane są modyfikacje danych. Zaczniemy od wstawiania, co jest najbardziej skomplikowaną operacją, następnie przejdziemy do aktualizacji i wreszcie usunięcia.
Wstawianie rekordu do zestawu rekordów
Zachowanie wstawiania zestawu rekordów typu Dynaset będzie zależeć od tego, jak program Access postrzega klucze tabeli bazowej. Będą 3 różne zachowania. Pierwsze dwa dotyczą obsługi kluczy podstawowych, które są w jakiś sposób automatycznie generowane przez serwer. Drugi jest szczególnym przypadkiem pierwszego zachowania, które ma zastosowanie tylko z backendem SQL Server przy użyciu IDENTITY
kolumna. Ostatni dotyczy przypadku, gdy klucze są dostarczane przez użytkownika (np. klucze naturalne w ramach wprowadzania danych). Zaczniemy od bardziej ogólnego przypadku kluczy generowanych przez serwer.
Wstawianie rekordu; tabela z kluczem podstawowym wygenerowanym przez serwer
Kiedy wstawiamy zestaw rekordów (ponownie, sposób, w jaki to robimy, za pośrednictwem interfejsu użytkownika programu Access lub VBA, nie ma znaczenia), program Access musi wykonać pewne czynności, aby dodać nowy wiersz do lokalnej pamięci podręcznej.
Należy zauważyć, że program Access ma różne zachowania wstawiania w zależności od konfiguracji klucza. W tym przypadku Cities
tabela nie ma IDENTITY
atrybut, ale raczej używa SEQUENCE
obiekt do wygenerowania nowego klucza. Oto sformatowany śledzony SQL:
SQLExecDirect:INSERT INTO "Application"."Cities" ("CityName" ,"StateProvinceID" ,"LatestRecordedPopulation" ,"LastEditedBy") WARTOŚCI ( ? ,? ,? ,?)SQLPrepare:SELECT "CityID" ,"CityName „ , „Identyfikator województwa”, „Lokalizacja”, „Najnowsza zarejestrowana populacja”, „Ostatnio edytowane przez”, „Ważne od”, „Ważne do” FROM „Aplikacja”. „Miasta”, GDZIE „ID miasta” JEST NULL . "Miasta". "Identyfikator Miasta" Z "Aplikacji". "Miasta" WHERE "Nazwa Miasta" =? ORAZ „Identyfikator Województwa Stanu” =? ORAZ „Najnowsza zarejestrowana populacja” =? AND "LastEditedBy" =?SQLExecute:(PRZEJDŹ DO ZAKŁADKI)SQLExecute:(FETCH MULTI-ROW)Pamiętaj, że program Access prześle tylko kolumny, które zostały faktycznie zmodyfikowane przez użytkownika. Mimo że samo zapytanie zawierało więcej kolumn, edytowaliśmy tylko 4 kolumny, więc program Access uwzględni tylko je. Zapewnia to, że program Access nie koliduje z domyślnym zachowaniem ustawionym dla innych kolumn, których użytkownik nie zmodyfikował, ponieważ program Access nie ma konkretnej wiedzy na temat obsługi tych kolumn przez źródło danych. Poza tym instrukcja insert jest prawie taka, jakiej byśmy się spodziewali.
Drugie stwierdzenie jest jednak nieco dziwne. Wybiera dla WHERE "CityID" IS NULL
. Wydaje się to niemożliwe, ponieważ wiemy już, że CityID
kolumna jest kluczem podstawowym i z definicji nie może mieć wartości null. Jeśli jednak spojrzysz na zrzut ekranu, nigdy nie zmieniliśmy CityID
kolumna. Z punktu widzenia programu Access jest NULL
. Najprawdopodobniej Access przyjmuje pesymistyczne podejście i nie zakłada, że źródło danych faktycznie będzie zgodne ze standardem SQL. Jak widzieliśmy w sekcji omawiającej, w jaki sposób program Access wybiera indeks, który ma być użyty do jednoznacznej identyfikacji wiersza, może to nie być klucz podstawowy, a jedynie UNIQUE
indeks, który może zezwolić na NULL
. W tym mało prawdopodobnym przypadku krańcowym wykonuje zapytanie tylko po to, aby upewnić się, że źródło danych nie utworzyło nowego rekordu o tej wartości. Po sprawdzeniu, że nie zostały zwrócone żadne dane, próbuje ponownie zlokalizować rekord za pomocą następującego filtra:
GDZIE "Nazwa miasta" =? ORAZ „Identyfikator Województwa Stanu” =? ORAZ „Najnowsza zarejestrowana populacja” =? ORAZ „LastEditedBy” =?które były tymi samymi 4 kolumnami, które użytkownik faktycznie zmodyfikował. Ponieważ istniało tylko jedno miasto o nazwie „Zeke”, otrzymaliśmy z powrotem tylko jeden rekord, a zatem program Access może następnie wypełnić lokalną pamięć podręczną nowym rekordem tymi samymi danymi, które zawiera źródło danych. Wprowadzi zmiany w innych kolumnach, ponieważ
SELECT
lista zawiera tylko CityID
klucz, którego następnie użyje w już przygotowanym oświadczeniu, aby następnie wypełnić cały wiersz za pomocą CityID
klawisz. Wstawianie rekordu; tabela z autoinkrementacją klucza podstawowego
Co jednak, jeśli tabela pochodzi z bazy danych SQL Server i zawiera kolumnę z autoinkrementacją, taką jak IDENTITY
atrybut? Access zachowuje się inaczej. Stwórzmy więc kopię Cities
tabeli, ale edytuj tak, aby CityID
kolumna jest teraz IDENTITY
kolumna.
Zobaczmy, jak program Access sobie z tym radzi:
SQLExecDirect:INSERT INTO „Application”. „Cities” ( „CityName”, „StateProvinceID”, „LatestRecordedPopulation”, „LastEditedBy”, „ValidFrom”, „ValidTo”) WARTOŚCI ( ? ,? ,? ,? ,? ,?)SQLExecDirect:SELECT @@IDENTITYSQLExecute:(PRZEJDŹ DO ZAKŁADKI)SQLExecute:(PRZEJDŹ DO ZAKŁADKI)Jest znacznie mniej paplaniny; po prostu robimy
SELECT @@IDENTITY
aby znaleźć nowo wstawioną tożsamość. Niestety nie jest to ogólne zachowanie. Na przykład MySQL obsługuje możliwość wykonania SELECT @@IDENTITY
, jednak program Access nie zapewni takiego zachowania. Sterownik PostgreSQL ODBC ma tryb emulacji SQL Server w celu nakłonienia Access do wysłania @@IDENTITY
do PostgreSQL, aby mógł mapować na odpowiednik serial
typ danych. Wstawianie rekordu z określoną wartością klucza podstawowego
Zróbmy trzeci eksperyment, używając tabeli z normalnym int
kolumna, bez IDENTITY
atrybut. Chociaż nadal będzie to klucz podstawowy w tabeli, będziemy chcieli zobaczyć, jak się zachowuje, gdy samodzielnie wstawimy klucz.
SQLExecDirect:INSERT INTO „Application”. „Cities” ( „CityID”, „CityName”, „StateProvinceID”, „LatestRecordedPopulation”, „LastEditedBy”, „ValidFrom”, „ValidTo”) WARTOŚCI ( ? ,? ,? ? Tym razem nie ma dodatkowej gimnastyki; ponieważ podaliśmy już wartość klucza podstawowego, program Access wie, że nie musi ponownie szukać wiersza; po prostu wykonuje przygotowaną instrukcję w celu ponownej synchronizacji wstawionego wiersza. Wracając do pierwotnego projektu, w którymCities
tabela użyłaSEQUENCE
obiekt do wygenerowania nowego klucza, możemy dodać funkcję VBA, aby pobrać nowy numer za pomocąNEXT VALUE FOR
i w ten sposób proaktywnie wypełnij klucz, aby uzyskać takie zachowanie. To dokładniej przybliża sposób działania aparatu bazy danych programu Access; jak tylko zabrudzimy rekord, pobiera nowy klucz zAutoNumber
typ danych, zamiast czekać, aż rekord zostanie faktycznie wstawiony. Tak więc, jeśli twoja baza danych używaSEQUENCE
lub innymi sposobami tworzenia kluczy, może się opłacać udostępnienie mechanizmu proaktywnego pobierania klucza, aby pomóc w wyeliminowaniu zgadywania, które robił Access w pierwszym przykładzie.Aktualizacja rekordu w zestawie rekordów
W przeciwieństwie do wstawek w poprzedniej sekcji, aktualizacje są stosunkowo łatwiejsze, ponieważ mamy już obecny klucz. W związku z tym Access zwykle zachowuje się bardziej prosto, jeśli chodzi o aktualizację. Istnieją dwa główne zachowania, które musimy wziąć pod uwagę podczas aktualizowania rekordu, które zależą od obecności kolumny rowversion.
Aktualizacja rekordu bez kolumny rowversion
Załóżmy, że modyfikujemy tylko jedną kolumnę. To właśnie widzimy w ODBC.
SQLExecute:(GOTO ZAKŁADKA)SQLExecDirect:UPDATE "Aplikacja"."Miasta"SET "Nazwa miasta"=?WHERE "Identyfikator miasta" =? ORAZ "Nazwa Miasta" =? ORAZ „Identyfikator Województwa Stanu” =? A „Lokalizacja” JEST NULL, A „LatestRecordedPopulation” =? ORAZ „LastEditedBy” =? ORAZ „WażnyOd” =? ORAZ „ValidTo” =?Hmm, o co chodzi z tymi wszystkimi dodatkowymi kolumnami, których nie zmieniliśmy? Cóż, znowu Access musi przyjąć pesymistyczny pogląd. Musi założyć, że ktoś mógł zmienić dane, gdy użytkownik powoli przeszukiwał edycje. Ale skąd Access miałby wiedzieć, że ktoś inny zmienił dane na serwerze? Cóż, logicznie rzecz biorąc, jeśli wszystkie kolumny są dokładnie takie same, to powinien był zaktualizować tylko jeden wiersz, prawda? Właśnie tego szuka program Access, porównując wszystkie kolumny; aby upewnić się, że aktualizacja wpłynie tylko na dokładnie jeden wiersz. Jeśli stwierdzi, że zaktualizował więcej niż jeden wiersz lub zero wierszy, wycofuje aktualizację i zwraca błąd lub#Deleted
do użytkownika.Ale… to trochę nieefektywne, prawda? Co więcej, może to powodować problemy, jeśli istnieje logika po stronie serwera, która może zmienić wartości wprowadzane przez użytkownika. Aby to zilustrować, załóżmy, że dodajemy głupi wyzwalacz, który zmienia nazwę miasta (oczywiście tego nie zalecamy):
CREATE TRIGGER SillyTriggerON Application.Cities PO UAKTUALNIENIU ASBEGIN UPDATE Application.Cities SET CityName ='zzzzz' WHERE EXISTS ( SELECT NULL FROM wstawiony jako i WHERE Cities.CityID =i.CityID );END;Jeśli więc spróbujemy zaktualizować wiersz, zmieniając nazwę miasta, będzie się wydawało, że się udało.Ale jeśli potem spróbujemy go edytować ponownie, otrzymamy komunikat o błędzie z odświeżoną wiadomością:
To są dane wyjściowe z pliku
sqlout.txt
:SQLExecDirect:UPDATE "Aplikacja"."Cities"SET "CityName"=?WHERE "CityID" =? ORAZ "Nazwa Miasta" =? ORAZ „Identyfikator Województwa Stanu” =? A „Lokalizacja” JEST NULL, A „LatestRecordedPopulation” =? ORAZ „LastEditedBy” =? ORAZ „WażnyOd” =? AND "ValidTo" =?SQLExecute:(PRZEJDŹ DO ZAKŁADKI)SQLExecute:(PRZEJDŹ DO ZAKŁADKI)SQLExecute:(WIELOKRZĘDOWY FETCH)SQLExecute:(WIELOWIERZOWY FETCH)Należy pamiętać, że drugaGOTO BOOKMARK
i kolejneMULTI-ROW FETCH
Nie wydarzyło się to, dopóki nie otrzymamy komunikatu o błędzie i go odrzuciliśmy. Powodem jest to, że gdy zabrudzimy rekord, Access wykonujeGOTO BOOKMARK
, zdaj sobie sprawę, że zwrócone dane nie pasują już do tego, co mają w pamięci podręcznej, co powoduje, że otrzymujemy komunikat „Dane zostały zmienione”. Zapobiega to marnowaniu czasu na edycję rekordu, który jest skazany na niepowodzenie, ponieważ jest już nieaktualny. Pamiętaj, że program Access w końcu odkryje zmianę, jeśli damy mu wystarczająco dużo czasu na odświeżenie danych. W takim przypadku nie byłoby komunikatu o błędzie; arkusz danych zostałby po prostu zaktualizowany, aby pokazać prawidłowe dane.Jednak w tych przypadkach Access miał odpowiedni klucz, więc nie miał problemu z odkryciem nowych danych. Ale czy to klucz jest kruchy? Jeśli wyzwalacz zmienił klucz podstawowy lub źródło danych ODBC nie reprezentowało wartości dokładnie tak, jak sądził Access, spowodowałoby to, że program Access zamalował rekord jako
#Deleted
ponieważ nie może wiedzieć, czy był edytowany przez serwer, czy ktoś inny, w porównaniu do tego, czy został legalnie usunięty przez kogoś innego.Aktualizacja rekordu za pomocą kolumny rowversion
Tak czy inaczej, otrzymanie komunikatu o błędzie lub
#Deleted
może być dość irytujące. Istnieje jednak sposób, aby uniknąć porównywania przez program Access wszystkich kolumn. Usuńmy wyzwalacz i dodajmy nową kolumnę:ALTER TABLE Application.CitiesADD RV rowversion NOT NULL;Dodajemyrowversion
który ma właściwość wystawiania się na ODBC jako mającySQLSpecialColumns(SQL_ROWVER)
, czyli to, co program Access musi wiedzieć, że może być używany jako sposób na wersję wiersza. Zobaczmy, jak aktualizacje działają z tą zmianą.SQLExecDirect:UPDATE „Aplikacja”. „Miasta” SET „Nazwa miasta”=? GDZIE "Identyfikator Miasta" =? AND "RV" =?SQLExecute:(PRZEJDŹ DO ZAKŁADKI)W przeciwieństwie do poprzedniego przykładu, w którym program Access porównywał wartość w każdej kolumnie, niezależnie od tego, czy użytkownik ją edytował, czy nie, aktualizujemy rekord tylko za pomocąRV
jako kryterium filtrowania. Rozumowanie jest takie, że jeśliRV
nadal ma taką samą wartość jak ta, którą przekazał program Access, program Access może mieć pewność, że ten wiersz nie był edytowany przez nikogo innego, ponieważ jeśli tak, toRV
jego wartość uległaby zmianie.Oznacza to również, że jeśli wyzwalacz zmienił dane lub jeśli SQL Server i Access nie reprezentowały jednej wartości dokładnie w ten sam sposób (np. liczby zmiennoprzecinkowe), program Access nie będzie się blokował, gdy ponownie wybierze zaktualizowany wiersz i wróci z inną wartości w innych kolumnach, których użytkownicy nie edytowali.
UWAGA :Nie wszystkie produkty DBMS będą używać tych samych terminów. Jako przykład,
timestamp
MySQL może być używany jako rowversion do celów ODBC. Musisz zapoznać się z dokumentacją produktu, aby sprawdzić, czy obsługują one funkcję rowversion, aby można było wykorzystać to zachowanie w programie Access.Widoki i wersja wierszy
Na widoki wpływa również obecność lub brak wersji rowversion. Załóżmy, że tworzymy widok w SQL Server z definicją:
CREATE VIEW dbo.vwCities ASSELECT CityID, CityNameFROM Application.Cities;Aktualizacja rekordu w widoku spowoduje powrót do porównania kolumna po kolumnie, tak jakby kolumna rowversion nie istniała w tabeli:SQLExecDirect:UPDATE "dbo". "vwCities" SET "Nazwa miasta"=? GDZIE "Identyfikator Miasta" =? ORAZ „Nazwa miasta” =?W związku z tym, jeśli potrzebujesz zachowania aktualizacji opartego na rowversion, należy zadbać o to, aby kolumny rowversion zostały uwzględnione w widokach. W przypadku widoku, który zawiera wiele tabel w złączeniach, najlepiej jest uwzględnić przynajmniej kolumny rowversion z tabel, w których zamierzasz aktualizować. Ponieważ zazwyczaj tylko jedna tabela może być aktualizowana, w tym tylko jedna wersja wiersza może wystarczyć jako ogólna zasada.Usuwanie rekordu w zestawie rekordów
Usunięcie rekordu zachowuje się podobnie do aktualizacji, a także użyje wersji rowversion, jeśli jest dostępna. Na stole bez rowversion otrzymujemy:
SQLExecDirect:USUŃ Z "Aplikacji". "Miasta" WHERE "CityID" =? ORAZ "Nazwa Miasta" =? ORAZ „Identyfikator Województwa Stanu” =? A „Lokalizacja” JEST NULL, A „LatestRecordedPopulation” =? ORAZ „LastEditedBy” =? ORAZ „WażnyOd” =? ORAZ „ValidTo” =?Na stole z rowversion otrzymujemy:SQLExecDirect:USUŃ Z "Aplikacji". "Miasta" WHERE "CityID" =? ORAZ „RV” =?Ponownie, Access musi podchodzić pesymistycznie do usuwania, ponieważ dotyczy aktualizacji; nie chciałby usunąć wiersza, który został zmieniony przez kogoś innego. W związku z tym wykorzystuje to samo zachowanie, które widzieliśmy podczas aktualizacji, aby chronić przed wieloma użytkownikami zmieniającymi te same rekordy.Wnioski
Dowiedzieliśmy się, jak program Access obsługuje modyfikacje danych i utrzymuje lokalną pamięć podręczną w synchronizacji ze źródłem danych ODBC. Widzieliśmy, jak pesymistyczny był Access, który był napędzany koniecznością obsługi jak największej liczby źródeł danych ODBC bez opierania się na konkretnych założeniach lub oczekiwaniach, że takie źródła danych ODBC będą obsługiwać określoną funkcję. Z tego powodu widzieliśmy, że Access będzie się zachowywał różnie w zależności od tego, jak zdefiniowano klucz dla danej tabeli połączonej ODBC. Gdybyśmy byli w stanie jawnie wstawić nowy klucz, wymagało to minimalnej pracy programu Access, aby ponownie zsynchronizować lokalną pamięć podręczną dla nowo wstawionego rekordu. Jeśli jednak pozwolimy serwerowi na wypełnienie klucza, program Access będzie musiał wykonać dodatkową pracę w tle, aby przeprowadzić ponowną synchronizację.
Widzieliśmy również, że posiadanie kolumny w tabeli, która może być używana jako wersja wiersza, może pomóc w ograniczeniu gadania między programem Access a źródłem danych ODBC podczas aktualizacji. Należy zapoznać się z dokumentacją sterownika ODBC, aby określić, czy obsługuje on wersję rowversion w warstwie ODBC, a jeśli tak, to uwzględnić taką kolumnę w tabelach lub widokach przed połączeniem z programem Access, aby czerpać korzyści z aktualizacji opartych na wersji rowversion.
Teraz wiemy, że w przypadku wszelkich aktualizacji lub usunięć program Access zawsze będzie próbował sprawdzić, czy wiersz nie został zmieniony od czasu ostatniego pobrania przez program Access, aby uniemożliwić użytkownikom wprowadzanie zmian, które mogą być nieoczekiwane. Musimy jednak wziąć pod uwagę skutki wynikające z wprowadzenia zmian w innych miejscach (np. wyzwalacz po stronie serwera, uruchomienie innego zapytania w innym połączeniu), które mogą spowodować, że Access uzna, że wiersz został zmieniony i w ten sposób nie zezwoli na zmianę. Te informacje pomogą nam przeanalizować i uniknąć tworzenia sekwencji modyfikacji danych, które mogą być sprzeczne z oczekiwaniami programu Access podczas ponownej synchronizacji lokalnej pamięci podręcznej.
W następnym artykule przyjrzymy się efektom zastosowania filtrów w zestawie rekordów.
Uzyskaj pomoc od naszych ekspertów ds. dostępu już dziś. Zadzwoń do naszego zespołu pod numer 773-809-5456 lub napisz do nas na adres [email protected].