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

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

Filtrování sady záznamů

V části 5 našeho seriálu se naučíme, jak Microsoft Access zpracovává implementované filtry a integruje je do dotazů ODBC. V předchozím článku jsme viděli, jak Access formuluje SELECT příkazy v ODBC SQL příkazech. V předchozím článku jsme také viděli, jak se Access pokusí aktualizovat řádek použitím WHERE doložka založená na klíči a případně na verzi řádku. Potřebujeme se však naučit, jak Access zpracuje filtry, které jsou poskytovány pro dotazy Accessu, a přeloží je do vrstvy ODBC. Existují různé přístupy, které může Access použít v závislosti na tom, jak jsou dotazy Accessu formulovány, a naučíte se, jak předvídat, jak Access přeloží Access dotaz na dotaz ODBC pro různé zadané predikáty filtru.

Bez ohledu na to, jak skutečně použijete filtr – ať už interaktivně pomocí příkazů pásu karet formuláře nebo datového listu nebo kliknutí pravým tlačítkem nebo programově pomocí VBA nebo spouštění uložených dotazů – Access zadá odpovídající dotaz ODBC SQL k provedení filtrování. Obecně se Access pokusí oddálit co nejvíce filtrování. Neřekne vám však, pokud to nemůže udělat. Pokud Access nemůže vyjádřit filtr pomocí syntaxe ODBC SQL, místo toho se pokusí provést filtrování sám stažením celého obsahu tabulky a lokálně vyhodnotit podmínku. To může vysvětlit, proč se někdy můžete setkat s dotazem, který běží rychle, ale s jednou malou změnou se zpomalí na procházení. Tato část vám doufejme pomůže porozumět tomu, kdy k tomu může dojít, a jak s tím zacházet, abyste mohli pomoci Access vzdálenému co nejvíce ke zdrojům dat pro použití filtru.

V tomto článku použijeme uložené dotazy, ale zde uvedené informace by se měly i nadále vztahovat na jiné metody použití filtrů.

Statické filtry

Začneme jednoduše a vytvoříme uložený dotaz s pevně zakódovaným filtrem.

SELECT 
   c.CityID
  ,c.CityName
  ,c.StateProvinceID
FROM Cities AS c
WHERE c.CityName="Boston";
Pokud otevřeme dotaz, uvidíme ve trasování toto ODBC SQL:

SQLExecDirect: 
SELECT "c"."CityID" 
FROM "Application"."Cities" "c" 
WHERE ("CityName" = 'Boston' ) 
Kromě změn v syntaxi se sémantika dotazu nezměnila; stejný filtr je předán tak, jak je. Všimněte si, že pouze CityID byla vybrána, protože dotaz ve výchozím nastavení používá sadu záznamů typu dynaset, o které jsme hovořili již v předchozí části.

Jednoduché parametrizované filtry

Změňme SQL tak, aby místo toho používal parametr:

PARAMETERS SelectedCityName Text ( 255 );
SELECT 
  c.CityID
 ,c.CityName
 ,c.StateProvinceID
FROM Cities AS c
WHERE c.CityName=[SelectedCityName];
Pokud spustíme dotaz a zadáme „Boston“ do hodnoty parametru prompt, jak je znázorněno, měli bychom vidět následující trasovací SQL ODBC:
SQLExecDirect: 
SELECT "c"."CityID" 
FROM "Application"."Cities" "c" 
WHERE ("CityName" =  ? ) 
Všimněte si, že stejné chování budeme pozorovat u odkazů na ovládací prvky nebo propojování podformulářů. Pokud bychom místo toho použili toto:

SELECT 
   c.CityID
  ,c.CityName
  ,c.StateProvinceID
FROM Cities AS c
WHERE c.CityName=[Forms]![frmSomeForm]![txtSomeText];
Stále bychom získali stejný trasovaný ODBC SQL, který jsme viděli s původním parametrizovaným dotazem. To stále platí, i když náš upravený dotaz neměl PARAMETERS prohlášení. To ukazuje, že Access je schopen rozpoznat, že takové řídicí reference, u kterých může být čas od času změněna jejich hodnota, jsou nejlépe považovány za parametr při formulování ODBC SQL.

To také funguje pro funkci VBA. Můžeme přidat novou funkci VBA:

Public Function GetSelectedCity() As String
    GetSelectedCity = "Boston"
End Function
Uložený dotaz upravíme tak, aby používal novou funkci VBA:

WHERE c.CityName=GetSelectedCity();
Pokud to vysledujete, uvidíte, že je to stále stejné. Ukázali jsme tedy, že bez ohledu na to, zda je vstup explicitním parametrem, odkazem na ovládací prvek nebo výsledkem funkce VBA, Access je bude všechny považovat za parametr dotazu ODBC SQL, který provede na našem jménem. To je dobrá věc, protože obecně dosáhneme lepšího výkonu, když můžeme znovu použít dotaz a jednoduše změnit parametr.

Existuje však ještě jeden běžný scénář, který vývojáři Accessu obvykle nastavují, a to vytváření dynamického SQL s kódem VBA, obvykle zřetězením řetězce a následným spuštěním zřetězeného řetězce. Použijme následující kód VBA:

Public Sub GetSelectedCities()
    Dim db As DAO.Database
    Dim rs As DAO.Recordset
    Dim fld As DAO.Field
    
    Dim SelectedCity As String
    Dim SQLStatement As String
    
    SelectedCity = InputBox("Enter a city name")
    SQLStatement = _
        "SELECT c.CityID, c.CityName, c.StateProvinceID " & _
        "FROM Cities AS c " & _
        "WHERE c.CityName = '" & SelectedCity & "';"
    
    Set db = CurrentDb
    Set rs = db.OpenRecordset(SQLStatement)
    Do Until rs.EOF
        For Each fld In rs.Fields
            Debug.Print fld.Value;
        Next
        Debug.Print
        rs.MoveNext
    Loop
End Sub
Trasované ODBC SQL pro OpenRecordset je následující:

SQLExecDirect: 
SELECT "c"."CityID" 
FROM "Application"."Cities" "c" 
WHERE ("CityName" = 'Boston' ) 
Na rozdíl od předchozích příkladů nebyl ODBC SQL parametrizován. Access nemá žádný způsob, jak zjistit, že „Boston“ byl dynamicky naplněn za běhu pomocí VBA.InputBox . Jednoduše jsme mu předali vytvořený SQL, který z POV Accessu je pouze statický SQL příkaz. V tomto případě porazíme parametrizaci dotazu. Je důležité si uvědomit, že populární rada poskytnutá vývojářům Accessu byla, že dynamicky konstruované SQL je lepší než použití parametrických dotazů, protože se tak vyhne problému, kdy může Access engine generovat plán provádění na základě jedné hodnoty parametru, která může být ve skutečnosti pro jinou suboptimální. hodnota parametru. Pro více podrobností o tomto jevu vám doporučuji přečíst si o problému „sniffování parametrů“. Všimněte si, že toto je obecný problém pro všechny databázové stroje, nejen pro Access. V případě Accessu však dynamické SQL fungovalo lépe, protože je mnohem levnější jen vygenerovat nový plán provádění. Naproti tomu motor RDBMS může mít další strategie pro řešení problému a může být citlivější na příliš mnoho jednorázových plánů provádění, protože to může negativně ovlivnit jeho ukládání do mezipaměti.

Z tohoto důvodu mohou být parametrizované dotazy z Accessu proti zdrojům ODBC vhodnější než dynamické SQL. Protože Access bude považovat ovládací prvky odkazů ve formuláři nebo funkcích jazyka VBA, které nevyžadují odkazy na sloupce, za parametry, nepotřebujete explicitní parametry ve zdrojích záznamů nebo zdrojích řádků. Pokud však ke spouštění SQL používáte VBA, je obvykle lepší použít ADO, které má také mnohem lepší podporu pro parametrizaci. V případě vytváření dynamického zdroje záznamů nebo zdroje řádků může být použití skrytého ovládacího prvku ve formuláři/sestavě snadným způsobem parametrizace dotazu. Pokud je však dotaz výrazně odlišný, vytvoření dynamického SQL ve VBA a jeho přiřazení k vlastnosti recordsource/rowsource efektivně vynutí úplnou rekompilaci, a proto se vyhnete použití špatných plánů provádění, které nebudou fungovat dobře pro aktuální sadu vstupů. Doporučení můžete najít v článku pojednávajícím o SQL Serveru WITH RECOMPILE užitečné při rozhodování, zda vynutit rekompilaci nebo použít parametrizovaný dotaz.

Použití funkcí při filtrování SQL

V předchozí části jsme viděli, že příkaz SQL obsahující funkci VBA byl parametrizován, takže Access mohl provést funkci VBA a použít výstup jako vstup do parametrizovaného dotazu. Ne všechny vestavěné funkce se však takto chovají. Použijme UCase() jako příklad filtrování dotazu. Dále použijeme funkci na sloupec.

SELECT 
   c.CityID
  ,c.CityName
  ,c.StateProvinceID
FROM Cities AS c
WHERE UCase([c].[CityName])="BOSTON";
Pokud se podíváme na trasovaný ODBC SQL, uvidíme toto:

SQLExecDirect: 
SELECT "c"."CityID" 
FROM "Application"."Cities" "c" 
WHERE ({fn ucase("CityName" )}= 'BOSTON' )
V předchozím příkladu byl Access schopen zcela parametrizovat GetSelectedCity() protože nevyžadoval žádné vstupy ze sloupců odkazovaných v dotazu. Nicméně UCase() vyžaduje vstup. Kdybychom poskytli UCase("Boston") , Access by to také parametrizoval. Vstupem je však odkaz na sloupec, který Access nemůže snadno parametrizovat. Access však může zjistit, že UCase() je jednou z podporovaných skalárních funkcí ODBC. Vzhledem k tomu, že upřednostňujeme vzdálenou komunikaci co možná nejvíce před zdrojem dat, Access to dělá vyvoláním verze ucase ODBC .

Pokud pak vytvoříme vlastní funkci VBA, která emuluje UCase() funkce:

Public Function MyUCase(InputValue As Variant) As String
    MyUCase = UCase(InputValue)
End Function
a změnil filtrování v dotazu na:

WHERE MyUCase([c].[CityName])="BOSTON";
Dostáváme toto:

SQLExecDirect: 
SELECT 
   "CityName"
  ,"c"."CityID" 
FROM "Application"."Cities" "c" 
Access nemůže vzdáleně vzdáleně ovládat vlastní funkci VBA MyUCase zpět ke zdroji dat. SQL uloženého dotazu je však legální, takže ho Access musí nějak splnit. Za tímto účelem to skončí stažením celé sady CityName a jeho odpovídající CityID za účelem předání funkce VBA MyUCase() a vyhodnotit výsledek. V důsledku toho nyní dotaz funguje mnohem pomaleji, protože Access nyní požaduje více dat a dělá více práce.

I když jsme použili UCase() na tomto příkladu jasně vidíme, že je obecně lepší oddálit co nejvíce práce ke zdroji dat. Co když ale máme komplexní funkci VBA, kterou nelze přepsat do nativního dialektu SQL zdroje dat? I když si myslím, že tento scénář je poměrně vzácný, stojí za zvážení. Předpokládejme, že můžeme přidat filtr, který zúží množinu vrácených měst.

SELECT 
   c.CityID
  ,c.CityName
  ,c.StateProvinceID
FROM Cities AS c
WHERE c.CityName LIKE "Bos*"
  AND MyUCase([c].[CityName])="BOSTON";
Trasované ODBC SQL bude vypadat takto:

SQLExecDirect: 
SELECT 
   "CityName"
  ,"c"."CityID" 
FROM "Application"."Cities" "c" 
WHERE ("CityName" LIKE 'Bos%' ) 
Přístup je schopen vzdáleného LIKE zpět ke zdroji dat, což vede k získání zpět mnohem menší datové sady. Stále bude provádět místní vyhodnocení MyUCase() na výsledném datovém souboru. Dotaz běží mnohem rychleji jednoduše kvůli menší vrácené datové sadě.

To nám říká, že pokud čelíme nežádoucímu scénáři, kdy nemůžeme snadno přefaktorovat složitou funkci VBA z dotazu, stále můžeme zmírnit špatné efekty přidáním filtrů, které lze vzdáleně snížit, aby se snížila počáteční sada záznamů, se kterými bude Access pracovat.

Poznámka k proměnlivosti

V předchozích příkladech jsme aplikovali skalární funkci na sloupec. To má potenciál vykreslit dotaz jako „neproměnitelný“, což znamená, že databázový stroj není schopen optimalizovat dotaz pomocí indexu pro vyhledávání a nalezení shod. Část „sarg“ slova „sargability“ odkazuje na „Search ARGument“. Předpokládejme, že máme index definovaný u zdroje dat v tabulce:

CREATE INDEX IX_Cities_CityName
ON Application.Cities (CityName);
Výrazy jako UCASE(CityName) zabraňuje databázovému stroji, aby mohl používat index IX_Cities_CityName protože modul je nucen vyhodnotit každý řádek jeden po druhém, aby našel shodu, stejně jako to udělal Access s vlastní funkcí VBA. Některé databázové stroje, jako jsou nejnovější verze SQL Server, podporují vytváření indexů na základě výrazu. Pokud bychom chtěli optimalizovat dotazy pomocí UCASE() transact-SQL, mohli bychom upravit definici indexu:

CREATE INDEX IX_Cities_Boston_Uppercase
ON Application.Cities (CityName)
WHERE UCASE(CityName) = 'BOSTON';
To umožňuje serveru SQL Server zpracovat dotaz pomocí WHERE UCase(CityName) = 'BOSTON' jako proměnlivý dotaz, protože nyní může používat index IX_Cities_Boston_Uppercase vrátit odpovídající záznamy. Pokud však dotaz odpovídal na 'CLEVELAND' místo 'BOSTON' , ztrácí se možnost prodlužování.

Bez ohledu na to, se kterým databázovým strojem skutečně pracujete, je vždy vhodnější navrhovat a používat variabilní dotazy, kdykoli je to možné, abyste se vyhnuli problémům s výkonem. Klíčové dotazy by měly mít krycí indexy, aby poskytovaly nejlepší výkon. Doporučuji vám, abyste si prostudovali více o proměnlivosti a indexech pokrytí, abyste se vyhnuli navrhování dotazů, které ve skutečnosti nelze protahovat.

Závěry

Zkontrolovali jsme, jak Access zpracovává použití filtrů z Access SQL na dotazy ODBC. Prozkoumali jsme také různé případy, kdy Access převede různé typy odkazů na parametr, což Accessu umožní provést vyhodnocení mimo vrstvu ODBC a předat je jako vstupy do připraveného příkazu ODBC. Podívali jsme se také na to, co se stane, když nelze parametrizovat, obvykle kvůli tomu, že jako vstupy obsahují odkazy na sloupce. To může mít důsledky na výkon během migrace na SQL server.

Pro určité funkce může být Access schopen převést výraz tak, aby místo toho používal skalární funkce ODBC, což Accessu umožňuje vzdálený výraz ke zdroji dat ODBC. Jedním z důsledků toho je, že pokud je implementace skalární funkce odlišná, může to způsobit, že se dotaz bude chovat jinak nebo může být rychlejší/pomalejší. Viděli jsme, jak může funkce VBA, dokonce i jednoduchá, která obaluje jinak vzdálenou skalární funkci, porazit snahy o vzdálený výraz. Dozvíme se také, že pokud nastane situace, kdy nemůžeme refaktorovat složitou funkci VBA z dotazu Access/recordsource/rowsource, můžeme alespoň zmírnit drahé stahování přidáním dalších filtrů na dotaz, které lze vzdáleně snížit a snížit tak množství. vrácených dat.

V příštím článku se podíváme na to, jak Access zpracovává připojení.

Hledáte pomoc s Microsoft Access? Zavolejte našim odborníkům ještě dnes na číslo 773-809-5456 nebo nám napište na adresu [email protected].


  1. Migrace DB pomocí průvodce NextForm Multi-Table Wizard

  2. The Zombie PerfMon Counter That Never Die!

  3. Ansible miluje PostgreSQL

  4. Jak nainstalovat ovladač pdo do obrazu php docker?