sql >> Databáze >  >> RDS >> Access

Jak Access komunikuje se zdroji dat ODBC? Část 4

Co dělá Access, když uživatel provede změny dat v propojené tabulce ODBC?

Naše série trasování ODBC pokračuje a v tomto čtvrtém článku vysvětlíme, jak vložit a aktualizovat datový záznam v sadě záznamů, a také proces odstranění záznamu. V předchozím článku jsme se dozvěděli, jak Access zpracovává naplnění dat ze zdrojů ODBC. Viděli jsme, že typ sady záznamů má důležitý vliv na to, jak bude Access formulovat dotazy na zdroj dat ODBC. Ještě důležitější je, že jsme zjistili, že se sadou záznamů typu dynaset provádí Access další práci, aby měl všechny informace potřebné k tomu, aby bylo možné vybrat jeden řádek pomocí klíče. To bude platit v tomto článku, kde prozkoumáme, jak se zachází s úpravami dat. Začneme vkládáním, což je nejsložitější operace, pak přejdeme k aktualizacím a nakonec k mazání.

Vložení záznamu do sady záznamů

Chování vkládání sady záznamů typu dynaset bude záviset na tom, jak Access vnímá klíče podkladové tabulky. Budou existovat 3 odlišná chování. První dva se zabývají zpracováním primárních klíčů, které jsou nějakým způsobem automaticky generovány serverem. Druhý je speciální případ prvního chování, který je použitelný pouze s backendem SQL Server pomocí IDENTITY sloupec. Poslední se zabývá případem, kdy klíče poskytuje uživatel (např. přirozené klíče jako součást zadávání dat). Začneme obecnějším případem klíčů generovaných serverem.

Vložení záznamu; tabulka s primárním klíčem generovaným serverem

Když vložíme sadu záznamů (opět, jak to uděláme, prostřednictvím uživatelského rozhraní Access nebo VBA nezáleží), Access musí udělat věci, aby přidal nový řádek do místní mezipaměti.

Důležitou věcí je poznamenat, že Access má různé chování vkládání v závislosti na tom, jak je klíč nastaven. V tomto případě Cities tabulka nemá IDENTITY atribut, ale spíše používá SEQUENCE objekt pro vygenerování nového klíče. Zde je formátovaný trasovaný 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)
Všimněte si, že Access odešle pouze sloupce, které byly skutečně upraveny uživatelem. I když samotný dotaz obsahoval více sloupců, upravili jsme pouze 4 sloupce, takže Access zahrne pouze ty. To zajišťuje, že Access nenaruší výchozí chování nastavené pro ostatní sloupce, které uživatel neupravil, protože Access nemá žádné konkrétní znalosti o tom, jak bude zdroj dat s těmito sloupci pracovat. Kromě toho je příkaz insert v podstatě to, co bychom očekávali.

Druhé tvrzení je však poněkud zvláštní. Vybere pro WHERE "CityID" IS NULL . Zdá se to nemožné, protože již víme, že CityID sloupec je primární klíč a podle definice nemůže být null. Pokud se však podíváte na snímek obrazovky, nikdy jsme neupravili CityID sloupec. Z POV aplikace Access je to NULL . Access s největší pravděpodobností zaujímá pesimistický přístup a nebude předpokládat, že zdroj dat bude ve skutečnosti dodržovat standard SQL. Jak jsme viděli v části pojednávající o tom, jak Access vybírá index, který má použít k jedinečné identifikaci řádku, nemusí to být primární klíč, ale pouze UNIQUE index, který může povolit NULL . Pro tento nepravděpodobný okrajový případ provede dotaz, jen aby se ujistil, že zdroj dat ve skutečnosti nevytvořil nový záznam s touto hodnotou. Jakmile zjistí, že nebyla vrácena žádná data, pokusí se záznam znovu najít pomocí následujícího filtru:

WHERE "CityName" = ?
  AND "StateProvinceID" = ?
  AND "LatestRecordedPopulation" = ?
  AND "LastEditedBy" = ?
což byly stejné 4 sloupce, které uživatel skutečně upravil. Vzhledem k tomu, že existovalo pouze jedno město s názvem „Zeke“, dostali jsme zpět pouze jeden záznam, a proto může Access naplnit místní mezipaměť novým záznamem stejnými daty, jaké má zdroj dat. Od SELECT začlení všechny změny do ostatních sloupců seznam obsahuje pouze CityID klíč, který pak použije ve svém již připraveném příkazu k vyplnění celého řádku pomocí CityID klíč.

Vložení záznamu; tabulka s autoinkrementujícím primárním klíčem

Co když však tabulka pochází z databáze SQL Server a má sloupec s automatickým přírůstkem, jako je IDENTITY atribut? Access se chová jinak. Vytvořme tedy kopii Cities tabulku, ale upravte tak, aby CityID sloupec je nyní IDENTITY sloupec.

Podívejme se, jak si s tím Access poradí:

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

SQLExecDirect:
SELECT @@IDENTITY

SQLExecute: (GOTO BOOKMARK)

SQLExecute: (GOTO BOOKMARK)
Je tu výrazně méně brblání; jednoduše provedeme SELECT @@IDENTITY najít nově vloženou identitu. Bohužel se nejedná o obecné chování. Například MySQL podporuje možnost provést SELECT @@IDENTITY Access však toto chování neposkytuje. Ovladač PostgreSQL ODBC má režim pro emulaci SQL Serveru, aby přiměl přístup k odeslání @@IDENTITY na PostgreSQL, aby se mohl mapovat na ekvivalentní serial datový typ.

Vložení záznamu s explicitní hodnotou primárního klíče

Udělejme 3. experiment pomocí tabulky s normálním int sloupec bez IDENTITY atribut. Ačkoli to bude stále primární klíč na stole, budeme chtít vidět, jak se chová, když klíč explicitně vložíme sami.

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

SQLExecute: (GOTO BOOKMARK)

SQLExecute: (MULTI-ROW FETCH)
Tentokrát se nekoná žádná extra gymnastika; protože jsme již poskytli hodnotu pro primární klíč, Access ví, že se nemusí pokoušet znovu najít řádek; pouze provede připravený příkaz pro resynchronizaci vloženého řádku. Vracíme se k původnímu návrhu, kde je Cities tabulka používá SEQUENCE objekt pro vygenerování nového klíče, můžeme přidat funkci VBA pro načtení nového čísla pomocí NEXT VALUE FOR a tak naplnit klíč proaktivně, abychom získali toto chování. To přesněji přibližuje, jak funguje databázový stroj Access; jakmile záznam zašpiníme, načte nový klíč z AutoNumber datový typ, spíše než čekat, až bude záznam skutečně vložen. Pokud tedy vaše databáze používá SEQUENCE nebo jinými způsoby vytváření klíčů, může se vyplatit poskytnout mechanismus pro aktivní načítání klíče, který pomůže vyhnout se dohadům, které jsme viděli dělat Access v prvním příkladu.

Aktualizace záznamu v sadě záznamů

Na rozdíl od insertů v předchozí části jsou aktualizace relativně jednodušší, protože klíč již máme. Access se tedy při aktualizaci obvykle chová přímočařeji. Při aktualizaci záznamu musíme vzít v úvahu dvě hlavní chování, která závisí na přítomnosti sloupce rowversion.

Aktualizace záznamu bez sloupce rowversion

Předpokládejme, že upravíme pouze jeden sloupec. To je to, co vidíme v 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" = ?
Hmm, jak je to se všemi těmi extra sloupci, které jsme neupravili? No, znovu, Access musí přijmout pesimistický výhled. Musí se předpokládat, že někdo mohl změnit data, zatímco uživatel pomalu tápal v úpravách. Ale jak by Access věděl, že někdo jiný změnil data na serveru? No, logicky, pokud jsou všechny sloupce úplně stejné, měl by se aktualizovat pouze jeden řádek, ne? To je to, co Access hledá, když porovnává všechny sloupce; abyste zajistili, že aktualizace ovlivní pouze jeden řádek. Pokud zjistí, že aktualizoval více než jeden řádek nebo nula řádků, vrátí aktualizaci a vrátí buď chybu nebo #Deleted uživateli.

Ale… to je trochu neefektivní, že? Kromě toho by to mohlo přinést problém, pokud existuje logika na straně serveru, která by mohla změnit hodnoty zadané uživatelem. Pro ilustraci předpokládejme, že přidáme hloupý spouštěč, který změní název města (samozřejmě to nedoporučujeme):

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;
Pokud se tedy pokusíme aktualizovat řádek změnou názvu města, bude se zdát, že se to podařilo.

Pokud se jej pak pokusíme upravit znovu, zobrazí se chybová zpráva s obnovenou zprávou:

Toto je výstup z 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)
Je důležité si uvědomit, že 2. GOTO BOOKMARK a následný MULTI-ROW FETCH es se nestalo, dokud jsme nedostali chybovou zprávu a neodmítli ji. Důvodem je, že když zašpiníme záznam, Access provede GOTO BOOKMARK uvědomíme si, že vrácená data již neodpovídají tomu, co mají v mezipaměti, což způsobí, že se nám zobrazí zpráva „Data byla změněna“. To nám brání ztrácet čas úpravou záznamu, který je odsouzen k neúspěchu, protože je již zastaralý. Všimněte si, že Access by také nakonec zjistil změnu, pokud bychom mu dali dostatek času na obnovení dat. V takovém případě by se neobjevila žádná chybová zpráva; datový list by se jednoduše aktualizoval, aby zobrazoval správná data.

V těchto případech však měl Access správný klíč, takže neměl problém objevit nová data. Ale pokud je to klíč, který je křehký? Pokud by spouštěč změnil primární klíč nebo zdroj dat ODBC nereprezentoval hodnotu přesně tak, jak si Access představoval, způsobilo by to, že Access vykreslí záznam jako #Deleted protože nemůže vědět, zda byla upravena serverem nebo někým jiným, a zda byla oprávněně smazána někým jiným.

Aktualizace záznamu se sloupcem rowversion

V obou případech se zobrazí chybová zpráva nebo #Deleted může být docela nepříjemné. Existuje však způsob, jak zabránit Accessu v porovnávání všech sloupců. Odstraníme spouštěč a přidáme nový sloupec:

ALTER TABLE Application.Cities
ADD RV rowversion NOT NULL;
Přidáme rowversion který má vlastnost vystavení ODBC jako SQLSpecialColumns(SQL_ROWVER) , což je to, co Access potřebuje vědět, že jej lze použít jako způsob verze řádku. Podívejme se, jak s touto změnou fungují aktualizace.

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

SQLExecute: (GOTO BOOKMARK)
Na rozdíl od předchozího příkladu, kde Access porovnával hodnoty v každém sloupci, ať už je uživatel upravil nebo ne, aktualizujeme záznam pouze pomocí RV jako kritérium filtru. Důvodem je, že pokud RV má stále stejnou hodnotu jako ten, který Access předal, pak si Access může být jistý, že tento řádek nebyl upraven nikým jiným, protože pokud ano, pak RV Hodnota ‘ by se změnila.

Znamená to také, že pokud spouštěč změnil data nebo pokud SQL Server a Access nereprezentovaly jednu hodnotu přesně stejným způsobem (např. plovoucí čísla), Access se nezastaví, když znovu vybere aktualizovaný řádek a vrátí se s odlišným hodnoty v jiných sloupcích, které uživatelé neupravili.

POZNÁMKA :Ne všechny produkty DBMS budou používat stejné výrazy. Například timestamp MySQL lze použít jako rowversion pro účely ODBC. Budete si muset prostudovat dokumentaci k produktu, abyste zjistili, zda podporují funkci rowversion, abyste toto chování mohli využít v aplikaci Access.

Zobrazení a verze řádků

Zobrazení jsou také ovlivněna přítomností nebo nepřítomností rowversion. Předpokládejme, že vytvoříme pohled na SQL Server s definicí:

CREATE VIEW dbo.vwCities AS
SELECT
	CityID,
	CityName
FROM Application.Cities;
Aktualizace záznamu v zobrazení by se vrátila k porovnání sloupců po sloupcích, jako by sloupec rowversion v tabulce neexistoval:

SQLExecDirect: UPDATE "dbo"."vwCities" SET "CityName"=?  WHERE "CityID" = ? AND "CityName" = ?
Pokud tedy potřebujete chování aktualizace založené na verzi řádku, měli byste se postarat o to, aby byly v zobrazeních zahrnuty sloupce verze řádku. V případě zobrazení, které obsahuje více tabulek ve spojeních, je nejlepší zahrnout alespoň sloupce verze řádku z tabulek, kde chcete aktualizovat. Protože obvykle lze aktualizovat pouze jednu tabulku, včetně pouze jedné verze řádku může stačit jako obecné pravidlo.

Odstranění záznamu v sadě záznamů

Odstranění záznamu se chová podobně jako aktualizace a také použije rowversion, pokud je k dispozici. Na stole bez rowversion dostaneme:

SQLExecDirect: 
DELETE FROM "Application"."Cities" 
WHERE "CityID" = ? 
  AND "CityName" = ? 
  AND "StateProvinceID" = ? 
  AND "Location" IS NULL 
  AND "LatestRecordedPopulation" = ? 
  AND "LastEditedBy" = ? 
  AND "ValidFrom" = ? 
  AND "ValidTo" = ?
Na tabulce s rowversion dostaneme:

SQLExecDirect: 
DELETE FROM "Application"."Cities" 
WHERE "CityID" = ? 
  AND "RV" = ?
Opět platí, že Access musí být ohledně mazání pesimistický, protože jde o aktualizaci; nebude chtít smazat řádek, který změnil někdo jiný. Používá tedy stejné chování, jaké jsme viděli při aktualizaci, aby se zabránilo tomu, že několik uživatelů změní stejné záznamy.

Závěry

Zjistili jsme, jak Access zpracovává úpravy dat a udržuje svou místní mezipaměť synchronizovanou se zdrojem dat ODBC. Viděli jsme, jak pesimistický byl Access, který byl veden nutností podporovat co nejvíce zdrojů dat ODBC, aniž bychom se spoléhali na konkrétní předpoklady nebo očekávání, že takové zdroje dat ODBC budou podporovat určitou funkci. Z tohoto důvodu jsme viděli, že Access se bude chovat odlišně v závislosti na tom, jak je klíč definován pro danou propojenou tabulku ODBC. Pokud jsme byli schopni explicitně vložit nový klíč, vyžadovalo to od Accessu minimální práci na opětovné synchronizaci místní mezipaměti pro nově vložený záznam. Pokud však serveru umožníme naplnit klíč, Access bude muset provést další práci na pozadí, aby se znovu synchronizoval.

Také jsme viděli, že sloupec v tabulce, který lze použít jako rowversion, může pomoci omezit chatování mezi Accessem a zdrojem dat ODBC při aktualizaci. Budete se muset podívat do dokumentace ovladače ODBC, abyste zjistili, zda podporuje rowversion na vrstvě ODBC, a pokud ano, zahrňte takový sloupec do tabulek nebo zobrazení před propojením s Accessem, abyste mohli využít výhod aktualizací založených na rowversion.

Nyní víme, že u všech aktualizací nebo odstranění se Access vždy pokusí ověřit, že řádek nebyl změněn od jeho posledního načtení aplikací Access, aby uživatelé nemohli provádět změny, které by mohly být neočekávané. Musíme však vzít v úvahu účinky vyplývající z provádění změn na jiných místech (např. spouštěč na straně serveru, spuštění jiného dotazu v jiném připojení), které mohou způsobit, že Access dojde k závěru, že řádek byl změněn, a tím změnu nepovolí. Tyto informace nám pomohou analyzovat a vyhnout se vytváření posloupnosti úprav dat, které mohou odporovat očekáváním Accessu, když znovu synchronizuje místní mezipaměť.

V příštím článku se podíváme na účinky použití filtrů na sadu záznamů.

Získejte pomoc od našich odborníků na přístup ještě dnes. Zavolejte našemu týmu na číslo 773-809-5456 nebo nám napište e-mail na adresu [email protected].


  1. Příklady SYSDATE() – MySQL

  2. Oracle Pivot dotaz poskytuje sloupce s uvozovkami kolem názvů sloupců. Co?

  3. Jak funguje sys.dm_exec_describe_first_result_set_for_object na serveru SQL Server

  4. vybrat * z tabulky vs vybrat colA, colB atd. z tabulky zajímavé chování v SQL Server 2005