Access
 sql >> база данни >  >> RDS >> Access

Как Access общува с ODBC източници на данни? част 4

Какво прави Access, когато потребител прави промени в данните в ODBC свързана таблица?

Нашата серия за проследяване на ODBC продължава и в тази четвърта статия ще обясним как да вмъкнете и актуализирате запис от данни в набор от записи, както и процеса на изтриване на запис. В предишната статия научихме как Access се справя с попълването на данни от ODBC източници. Видяхме, че типът на набора от записи има важен ефект върху начина, по който Access ще формулира заявките към източника на ODBC данни. По-важното е, че открихме, че с набор от записи от тип dynaset, Access извършва допълнителна работа, за да разполага с цялата информация, необходима, за да може да избере един ред с помощта на ключ. Това ще се прилага в тази статия, където изследваме как се обработват модификациите на данните. Ще започнем с вмъкване, което е най-сложната операция, след което ще преминем към актуализации и накрая изтривания.

Вмъкване на запис в набор от записи

Поведението при вмъкване на набор от записи от тип dynaset ще зависи от това как Access възприема ключовете на основната таблица. Ще има 3 различни поведения. Първите две се занимават с обработка на първични ключове, които се генерират автоматично от сървъра по някакъв начин. Вторият е специален случай на първото поведение, което е приложимо само с бекенда на SQL Server, използващ IDENTITY колона. Последният се занимава със случая, когато ключовете са предоставени от потребителя (например естествени ключове като част от въвеждането на данни). Ще започнем с по-общия случай на генерирани от сървъра ключове.

Вмъкване на запис; таблица с генериран от сървъра първичен ключ

Когато вмъкнем набор от записи (отново, как правим това, чрез Access UI или VBA няма значение), Access трябва да направи неща, за да добави новия ред към локалния кеш.

Важното нещо, което трябва да се отбележи, е, че Access има различно поведение при вмъкване в зависимост от това как е настроен ключът. В този случай Cities таблицата няма IDENTITY атрибут, а по-скоро използва SEQUENCE обект за генериране на нов ключ. Ето форматирания проследен SQL:

SQLExecDirect:
INSERT INTO  "Application"."Cities"  (
   "CityName"
  ,"StateProvinceID"
  ,"LatestRecordedPopulation"
  ,"LastEditedBy"
) VALUES (
   ?
  ,?
  ,?
  ,?)

SQLPrepare:
SELECT
   "CityID"
  ,"CityName"
  ,"StateProvinceID"
  ,"Location"
  ,"LatestRecordedPopulation"
  ,"LastEditedBy"
  ,"ValidFrom"
  ,"ValidTo"
FROM "Application"."Cities"
WHERE "CityID" IS NULL

SQLExecute: (GOTO BOOKMARK)

SQLExecDirect:
SELECT
  "Application"."Cities"."CityID"
FROM "Application"."Cities"
WHERE "CityName" = ?
  AND "StateProvinceID" = ?
  AND "LatestRecordedPopulation" = ?
  AND "LastEditedBy" = ?

SQLExecute: (GOTO BOOKMARK)

SQLExecute: (MULTI-ROW FETCH)
Имайте предвид, че Access ще изпраща само колони, които действително са били променени от потребителя. Въпреки че самата заявка включваше повече колони, ние редактирахме само 4 колони, така че Access ще включва само тях. Това гарантира, че Access няма да пречи на поведението по подразбиране, зададено за другите колони, които потребителят не е променил, тъй като Access няма конкретни познания за това как източникът на данни ще обработва тези колони. Освен това, операторът за вмъкване е почти това, което бихме очаквали.

Второто твърдение обаче е малко странно. Избира за WHERE "CityID" IS NULL . Това изглежда невъзможно, тъй като вече знаем, че CityID колоната е първичен ключ и по дефиниция не може да бъде нула. Въпреки това, ако погледнете екранната снимка, ние никога не сме променяли CityID колона. От Access’ POV е NULL . Най-вероятно Access възприема песимистичен подход и няма да приеме, че източникът на данни всъщност ще се придържа към SQL стандарта. Както видяхме от раздела, обсъждащ как Access избира индекс, който да използва за уникално идентифициране на ред, той може да не е първичен ключ, а просто UNIQUE индекс, който може да позволи NULL . За този малко вероятен крайен случай той прави заявка, само за да се увери, че източникът на данни всъщност не е създал нов запис с тази стойност. След като провери дали няма върнати данни, след това се опитва да намери отново записа със следния филтър:

WHERE "CityName" = ?
  AND "StateProvinceID" = ?
  AND "LatestRecordedPopulation" = ?
  AND "LastEditedBy" = ?
които бяха същите 4 колони, които потребителят всъщност промени. Тъй като имаше само един град на име „Zeke“, получихме само един запис обратно и по този начин Access може след това да попълни локалния кеш с новия запис със същите данни, каквито има източникът на данни. Той ще включва всички промени в други колони, тъй като SELECT списъкът включва само CityID ключ, който след това ще използва в вече подготвеното си изявление, за да попълни целия ред с помощта на CityID ключ.

Вмъкване на запис; таблица с автоматично нарастващ първичен ключ

Но какво ще стане, ако таблицата идва от база данни на SQL Server и има колона за автоматично нарастване, като например IDENTITY атрибут? Достъпът се държи по различен начин. Така че нека създадем копие на Cities таблица, но редактирайте така, че CityID колоната вече е IDENTITY колона.

Нека видим как Access се справя с това:

SQLExecDirect:
INSERT INTO "Application"."Cities" (
   "CityName"
  ,"StateProvinceID"
  ,"LatestRecordedPopulation"
  ,"LastEditedBy"
  ,"ValidFrom"
  ,"ValidTo"
) VALUES (
   ?
  ,?
  ,?
  ,?
  ,?
  ,?)

SQLExecDirect:
SELECT @@IDENTITY

SQLExecute: (GOTO BOOKMARK)

SQLExecute: (GOTO BOOKMARK)
Има значително по-малко бърборене; ние просто правим SELECT @@IDENTITY за да намерите нововъведената самоличност. За съжаление това не е общоприето поведение. Например MySQL поддържа възможността за извършване на SELECT @@IDENTITY , обаче Access няма да осигури това поведение. PostgreSQL ODBC драйверът има режим за емулиране на SQL Server, за да подмами Access да изпрати @@IDENTITY към PostgreSQL, така че да може да се съпостави с еквивалентния serial тип данни.

Вмъкване на запис с изрична стойност за първичен ключ

Нека направим трети експеримент, използвайки таблица с нормален int колона, без IDENTITY атрибут. Въпреки че все още ще бъде първичен ключ в таблицата, ще искаме да видим как се държи, когато сами изрично вмъкнем ключа.

SQLExecDirect:
INSERT INTO  "Application"."Cities" (
   "CityID"
  ,"CityName"
  ,"StateProvinceID"
  ,"LatestRecordedPopulation"
  ,"LastEditedBy"
  ,"ValidFrom"
  ,"ValidTo"
) VALUES (
   ?
  ,?
  ,?
  ,?
  ,?
  ,?
  ,?
)

SQLExecute: (GOTO BOOKMARK)

SQLExecute: (MULTI-ROW FETCH)
Този път няма допълнителна гимнастика; тъй като вече предоставихме стойността за първичния ключ, Access знае, че не е нужно да се опитва да намери реда отново; той просто изпълнява подготвения оператор за повторно синхронизиране на вмъкнатия ред. Връщайки се към оригиналния дизайн, където Cities таблица използва SEQUENCE обект за генериране на нов ключ, можем да добавим VBA функция за извличане на новия номер, използвайки NEXT VALUE FOR и по този начин попълваме ключа проактивно, за да ни осигури това поведение. Това по-близо се доближава до начина, по който работи механизмът на базата данни на Access; веднага щом изцапаме запис, той извлича нов ключ от AutoNumber тип данни, вместо да чакате, докато записът действително бъде вмъкнат. По този начин, ако вашата база данни използва SEQUENCE или други начини за създаване на ключове, може да се изплати да осигурим механизъм за проактивно извличане на ключа, за да помогнем за премахването на догадките, които видяхме, че Access прави с първия пример.

Актуализиране на запис в набор от записи

За разлика от вмъкванията в предишния раздел, актуализациите са относително по-лесни, тъй като вече разполагаме с ключа. По този начин Access обикновено се държи по-ясно, когато става въпрос за актуализиране. Има две основни поведения, които трябва да вземем предвид при актуализиране на запис, което зависи от наличието на колона с версия на реда.

Актуализиране на запис без колона с версия на реда

Да предположим, че променяме само една колона. Това виждаме в ODBC.

SQLExecute: (GOTO BOOKMARK)

SQLExecDirect:
UPDATE "Application"."Cities"
SET "CityName"=?
WHERE "CityID" = ?
  AND "CityName" = ?
  AND "StateProvinceID" = ?
  AND "Location" IS NULL
  AND "LatestRecordedPopulation" = ?
  AND "LastEditedBy" = ?
  AND "ValidFrom" = ?
  AND "ValidTo" = ?
Хм, какво става с всички тези допълнителни колони, които не сме променили? Е, отново, Access трябва да приеме песимистична перспектива. Трябва да се предположи, че някой евентуално би могъл да е променил данните, докато потребителят бавно се рови в редакциите. Но как Access ще знае, че някой друг е променил данните на сървъра? Е, логично, ако всички колони са абсолютно еднакви, тогава трябваше да е актуализиран само един ред, нали? Това е, което Access търси, когато сравнява всички колони; за да се гарантира, че актуализацията само ще засегне точно един ред. Ако установи, че е актуализирал повече от един ред или нула реда, той връща актуализацията и връща грешка или #Deleted на потребителя.

Но... това е някак неефективно, нали? Освен това, това може да доведе до проблем, ако има логика от страна на сървъра, която може да промени стойностите, въведени от потребителя. За да илюстрираме, да предположим, че добавяме глупав тригер, който променя името на града (разбира се, не препоръчваме това):

CREATE TRIGGER SillyTrigger
ON Application.Cities AFTER UPDATE AS
BEGIN
  UPDATE Application.Cities
  SET CityName = 'zzzzz'
  WHERE EXISTS (
    SELECT NULL
    FROM inserted AS i
    WHERE Cities.CityID = i.CityID
  );
END;
Така че, ако след това се опитаме да актуализираме ред, като променим името на града, ще изглежда, че е успял.

Но ако след това се опитаме да го редактираме отново, получаваме съобщение за грешка с обновено съобщение:

Това е изходът от sqlout.txt :

SQLExecDirect:
UPDATE "Application"."Cities"
SET "CityName"=?
WHERE "CityID" = ?
  AND "CityName" = ?
  AND "StateProvinceID" = ?
  AND "Location" IS NULL
  AND "LatestRecordedPopulation" = ?
  AND "LastEditedBy" = ?
  AND "ValidFrom" = ?
  AND "ValidTo" = ?

SQLExecute: (GOTO BOOKMARK)

SQLExecute: (GOTO BOOKMARK)

SQLExecute: (MULTI-ROW FETCH)

SQLExecute: (MULTI-ROW FETCH)
Важно е да се отбележи, че вторият GOTO BOOKMARK и последващото MULTI-ROW FETCH es не се случи, докато не получим съобщението за грешка и не го отхвърлим. Причината е, че докато замърсяваме запис, Access извършва GOTO BOOKMARK , осъзнаваме, че върнатите данни вече не съвпадат с това, което имат в кеша, което ни кара да получим съобщението „Данните са променени“. Това ни пречи да губим време за редактиране на запис, който е обречен на провал, защото вече е остарял. Имайте предвид, че Access в крайна сметка също ще открие промяната, ако му дадем достатъчно време за опресняване на данните. В този случай няма да има съобщение за грешка; листът с данни просто ще бъде актуализиран, за да покаже правилните данни.

В тези случаи обаче Access имаше правилния ключ, така че нямаше проблем с откриването на новите данни. Но ако ключът е крехък? Ако тригерът е променил първичния ключ или източникът на ODBC данни не представлява стойността точно както Access смяташе, че ще бъде, това би накарало Access да нарисува записа като #Deleted тъй като не може да знае дали е редактирано от сървъра или някой друг спрямо това дали е законно изтрито от някой друг.

Актуализиране на запис с колона версия на реда

Така или иначе, получаване на съобщение за грешка или #Deleted може да бъде доста досадно. Но има начин да избегнете Access да сравнява всички колони. Нека премахнем тригера и добавим нова колона:

ALTER TABLE Application.Cities
ADD RV rowversion NOT NULL;
Добавяме rowversion който има свойството да бъде изложен на ODBC като имащ SQLSpecialColumns(SQL_ROWVER) , което Access трябва да знае, че може да се използва като начин за версия на реда. Нека да разгледаме как работят актуализациите с тази промяна.

SQLExecDirect: UPDATE "Application"."Cities" 
SET "CityName"=?  
WHERE "CityID" = ? 
  AND "RV" = ?

SQLExecute: (GOTO BOOKMARK)
За разлика от предишния пример, в който Access сравнява стойността във всяка колона, независимо дали потребителят я е редактирал или не, ние актуализираме записа само с помощта на RV като критерии за филтриране. Причината е, че ако RV все още има същата стойност като тази, която Access е прехвърлил, тогава Access може да бъде уверен, че този ред не е редактиран от никой друг, защото ако е бил, тогава RV Стойността на 's би се променила.

Това също така означава, че ако тригер промени данните или ако SQL Server и Access не представляват една стойност по точно същия начин (например плаващи числа), Access няма да се откаже, когато избере отново актуализирания ред и се връща с различни стойности в други колони, които потребителите не са редактирали.

ЗАБЕЛЕЖКА :Не всички продукти на СУБД ще използват едни и същи термини. Като пример, timestamp на MySQL може да се използва като версия на ред за целите на ODBC. Ще трябва да се консултирате с документацията на продукта, за да видите дали поддържа функцията за версия на реда, за да можете да използвате това поведение с Access.

Изгледи и версия на реда

Изгледите също са повлияни от наличието или отсъствието на версия на реда. Да предположим, че създаваме изглед в SQL Server с дефиницията:

CREATE VIEW dbo.vwCities AS
SELECT
	CityID,
	CityName
FROM Application.Cities;
Актуализирането на запис в изгледа ще се върне към сравнението колона по колона, сякаш колоната версия на реда не съществува в таблицата:

SQLExecDirect: UPDATE "dbo"."vwCities" SET "CityName"=?  WHERE "CityID" = ? AND "CityName" = ?
Следователно, ако имате нужда от поведение на актуализация, базирано на версия на реда, трябва да се погрижите да се уверите, че колоните за версия на реда са включени в изгледите. В случай на изглед, който съдържа множество таблици в обединения, най-добре е да включите поне колоните с версия на реда от таблица(и), където възнамерявате да актуализирате. Тъй като обикновено само една таблица може да бъде актуализирана, включително само една версия на ред може да е достатъчна като общо правило.

Изтриване на запис в набор от записи

Изтриването на запис се държи подобно на актуализациите и също така ще използва версия на ред, ако е налична. На таблица без версия на ред получаваме:

SQLExecDirect: 
DELETE FROM "Application"."Cities" 
WHERE "CityID" = ? 
  AND "CityName" = ? 
  AND "StateProvinceID" = ? 
  AND "Location" IS NULL 
  AND "LatestRecordedPopulation" = ? 
  AND "LastEditedBy" = ? 
  AND "ValidFrom" = ? 
  AND "ValidTo" = ?
На таблица с версия на ред получаваме:

SQLExecDirect: 
DELETE FROM "Application"."Cities" 
WHERE "CityID" = ? 
  AND "RV" = ?
Отново, Access трябва да бъде песимист по отношение на изтриването, тъй като става дума за актуализиране; не би искал да изтрие ред, който е променен от някой друг. По този начин той използва същото поведение, което видяхме при актуализирането, за да се предпази от множество потребители, които променят едни и същи записи.

Заключения

Научихме как Access обработва модификациите на данните и поддържа локалния си кеш в синхрон с източника на данни ODBC. Видяхме колко песимистичен беше Access, който беше воден от необходимостта да се поддържат възможно най-много източници на ODBC данни, без да се разчита на конкретни предположения или очаквания, че такива източници на ODBC данни ще поддържат определена функция. Поради тази причина видяхме, че Access ще се държи различно в зависимост от това как е дефиниран ключът за дадена ODBC свързана таблица. Ако бяхме в състояние изрично да вмъкнем нов ключ, това изискваше минимална работа от Access за повторно синхронизиране на локалния кеш за нововмъкнатия запис. Ако обаче позволим на сървъра да попълни ключа, Access ще трябва да извърши допълнителна работа във фонов режим, за да се синхронизира отново.

Видяхме също, че наличието на колона в таблицата, която може да се използва като версия на ред, може да помогне за намаляване на бърборенето между Access и ODBC източника на данни при актуализация. Ще трябва да се консултирате с документацията на ODBC драйвера, за да определите дали поддържа версията на реда на ODBC слоя и ако е така, включете такава колона в таблиците или изгледите, преди да се свържете с Access, за да се възползвате от предимствата на базираните на версии на реда актуализации.

Вече знаем, че за всякакви актуализации или изтривания, Access винаги ще се опитва да провери дали редът не е променен от последното му извличане от Access, за да попречи на потребителите да правят промени, които може да са неочаквани. Трябва обаче да вземем предвид ефектите, произтичащи от извършването на промени на други места (например тригер от страна на сървъра, изпълнение на различна заявка в друга връзка), които могат да накарат Access да заключи, че редът е променен и по този начин да забрани промяната. Тази информация ще ни помогне да анализираме и да избегнем създаването на поредица от модификации на данни, които могат да противоречат на очакванията на Access, когато той повторно синхронизира локалния кеш.

В следващата статия ще разгледаме ефектите от прилагането на филтри върху набор от записи.

Получете помощ от нашите експерти по достъп днес. Обадете се на нашия екип на 773-809-5456 или ни изпратете имейл на [email protected].


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Разплитане на новите ODBC и OLEDB драйвери на Microsoft SQL Server

  2. Присъединете се към нас за Microsoft Access с SQL Server Academy Част II

  3. Защо първичните ключове са важни и как да изберем един

  4. Как да направите вашите бази данни за достъп изключително бързи!

  5. Access 2021 For Dummies Cheat Sheet