レコードセットのフィルタリング
シリーズのパート5では、Microsoft Accessが実装されたフィルターを処理し、それらをODBCクエリに統合する方法を学習します。前回の記事では、AccessがSELECT
をどのように作成するかを見ました。 ODBCSQLコマンドのステートメント。また、前回の記事で、AccessがWHERE
を適用して行を更新しようとする方法も確認しました。 キーと、該当する場合はrowversionに基づく句。ただし、AccessがAccessクエリに提供されるフィルターを処理し、ODBCレイヤーでそれらを変換する方法を学習する必要があります。 Accessクエリがどのように定式化されるかに応じて、Accessが使用できるさまざまなアプローチがあり、AccessがAccessクエリをさまざまなフィルタ述語のODBCクエリに変換する方法を予測する方法を学習します。
フォームやデータシートのリボンコマンドや右メニュークリックを介してインタラクティブに、またはプログラムでVBAを使用したり、保存されたクエリを実行したりするなど、実際にフィルタを適用する方法に関係なく、Accessは対応するODBCSQLクエリを発行してフィルタリングを実行します。一般に、Accessは可能な限りフィルタリングをリモート化しようとします。ただし、それができないかどうかはわかりません。代わりに、AccessがODBC SQL構文を使用してフィルターを表現できない場合は、代わりに、テーブルの内容全体をダウンロードしてローカルで条件を評価することにより、フィルター自体を実行しようとします。これは、すばやく実行されるクエリに遭遇することがある理由を説明できますが、1つの小さな変更で、クロールが遅くなります。このセクションが、これがいつ発生する可能性があるか、およびそれを処理する方法を理解し、フィルターを適用するためにデータソースに可能な限りリモートでアクセスできるようにするのに役立つことを願っています。
この記事では、保存されたクエリを使用しますが、ここで説明する情報は、フィルターを適用する他の方法にも適用されます。
静的フィルター
簡単に始めて、ハードコードされたフィルターを使用して保存されたクエリを作成します。
SELECT c.CityID ,c.CityName ,c.StateProvinceID FROM Cities AS c WHERE c.CityName="Boston";クエリを開くと、このODBCSQLがトレースに表示されます。
SQLExecDirect: SELECT "c"."CityID" FROM "Application"."Cities" "c" WHERE ("CityName" = 'Boston' )構文の変更を除いて、クエリのセマンティクスは変更されていません。同じフィルターがそのまま渡されます。
CityID
のみに注意してください デフォルトでは、クエリは前のセクションですでに説明したダイナセットタイプのレコードセットを使用するため、が選択されました。 単純なパラメーター化されたフィルター
代わりにパラメータを使用するようにSQLを変更しましょう:
PARAMETERS SelectedCityName Text ( 255 ); SELECT c.CityID ,c.CityName ,c.StateProvinceID FROM Cities AS c WHERE c.CityName=[SelectedCityName];クエリを実行し、示されているようにパラメータプロンプト値に「Boston」を入力すると、次のODBCトレースSQLが表示されます。
SQLExecDirect: SELECT "c"."CityID" FROM "Application"."Cities" "c" WHERE ("CityName" = ? )コントロール参照またはサブフォームリンクでも同じ動作が見られることに注意してください。代わりにこれを使用した場合:
SELECT c.CityID ,c.CityName ,c.StateProvinceID FROM Cities AS c WHERE c.CityName=[Forms]![frmSomeForm]![txtSomeText];元のパラメーター化されたクエリで見たのと同じトレースされたODBCSQLを引き続き取得します。変更されたクエリに
PARAMETERS
がなかったとしても、それは依然として当てはまります。 声明。これは、Accessが、値が時々変更される可能性のあるそのような制御参照が、ODBCSQLを作成するときにパラメーターとして最も適切に扱われることを認識できることを示しています。 これはVBA機能でも機能します。新しいVBA関数を追加できます:
Public Function GetSelectedCity() As String GetSelectedCity = "Boston" End Function保存したクエリを調整して、新しいVBA関数を使用します。
WHERE c.CityName=GetSelectedCity();これをトレースすると、それがまだ同じであることがわかります。したがって、入力が明示的なパラメーター、コントロールへの参照、またはVBA関数の結果であるかどうかに関係なく、AccessはそれらすべてをODBCSQLクエリのパラメーターとして処理して実行することを示しました。に代わって。クエリを再利用してパラメータを変更するだけで、一般的にパフォーマンスが向上するため、これは良いことです。
ただし、Access開発者が通常設定するもう1つの一般的なシナリオがあります。これは、通常、文字列を連結してから連結された文字列を実行することにより、VBAコードを使用して動的SQLを作成することです。次の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
OpenRecordset
のトレースされたODBCSQL 次のとおりです: SQLExecDirect: SELECT "c"."CityID" FROM "Application"."Cities" "c" WHERE ("CityName" = 'Boston' )前の例とは異なり、ODBCSQLはパラメーター化されていません。 Accessには、「ボストン」が実行時に
VBA.InputBox
によって動的に入力されたことを知る方法がありません。 。 AccessのPOVから、単なる静的SQLステートメントである構築されたSQLを渡しただけです。この場合、クエリのパラメータ化を無効にします。 Access開発者に与えられた一般的なアドバイスは、動的に構築されたSQLは、パラメータクエリを使用するよりも優れているということを認識することが重要です。これは、Accessエンジンが1つのパラメータ値に基づいて実行プランを生成し、実際には別のパラメータ値には最適ではない可能性があるという問題を回避するためです。パラメータ値。この現象の詳細については、「パラメータスニッフィング」の問題をお読みになることをお勧めします。これは、Accessだけでなく、すべてのデータベースエンジンの一般的な問題であることに注意してください。ただし、Accessの場合、新しい実行プランを生成するだけの方がはるかに安価であるため、動的SQLの方がうまく機能しました。対照的に、RDBMSエンジンには、問題を処理するための追加の戦略があり、キャッシュに悪影響を与える可能性があるため、1回限りの実行計画が多すぎることに敏感になる可能性があります。
そのため、動的SQLよりもODBCソースに対するAccessからのパラメーター化されたクエリの方が適している場合があります。 Accessは、列参照を必要としないフォームまたはVBA関数の参照コントロールをパラメーターとして扱うため、レコードソースまたは行ソースに明示的なパラメーターは必要ありません。ただし、VBAを使用してSQLを実行している場合は、通常、パラメータ化のサポートもはるかに優れているADOを使用することをお勧めします。動的なレコードソースまたは行ソースを作成する場合、フォーム/レポートで非表示のコントロールを使用すると、クエリをパラメーター化する簡単な方法になります。ただし、クエリが著しく異なる場合は、VBAで動的SQLを構築し、それをrecordsource / rowsourceプロパティに割り当てると、完全な再コンパイルが効果的に強制されるため、現在の入力セットでうまく機能しない不適切な実行プランの使用を回避できます。 SQLServerのWITH RECOMPILE
について説明している記事で推奨事項を見つけることができます。 パラメータ化されたクエリを使用するのではなく、再コンパイルを強制するかどうかを決定するのに役立ちます。
SQLフィルタリングでの関数の使用
前のセクションでは、AccessがVBA関数を実行し、出力をパラメーター化されたクエリへの入力として使用できるように、VBA関数を含むSQLステートメントがパラメーター化されていることを確認しました。ただし、すべての組み込み関数がこのように動作するわけではありません。 UCase()
を使用しましょう クエリをフィルタリングする例として。さらに、この関数を列に適用します。
SELECT c.CityID ,c.CityName ,c.StateProvinceID FROM Cities AS c WHERE UCase([c].[CityName])="BOSTON";トレースされたODBCSQLを見ると、次のことがわかります。
SQLExecDirect: SELECT "c"."CityID" FROM "Application"."Cities" "c" WHERE ({fn ucase("CityName" )}= 'BOSTON' )前の例では、Accessは
GetSelectedCity()
を完全にパラメーター化することができました。 クエリで参照される列からの入力は必要ないためです。ただし、UCase()
入力が必要です。 UCase("Boston")
を提供していましたか 、Accessもこれをパラメータ化していたでしょう。ただし、入力は列参照であり、Accessはこれを簡単にパラメーター化できません。ただし、AccessはUCase()
を検出できます サポートされているODBCスカラー関数の1つです。データソースに可能な限りリモーティングすることを好むため、AccessはODBCのバージョンのucase
を呼び出すことでそれを実行します。 。
次に、UCase()
をエミュレートするカスタムVBA関数を作成します。 機能:
Public Function MyUCase(InputValue As Variant) As String MyUCase = UCase(InputValue) End Functionクエリのフィルタリングを次のように変更しました:
WHERE MyUCase([c].[CityName])="BOSTON";これが私たちが得るものです:
SQLExecDirect: SELECT "CityName" ,"c"."CityID" FROM "Application"."Cities" "c"アクセスはカスタムVBA関数
MyUCase
をリモートできません データソースに戻ります。ただし、保存されたクエリのSQLは有効であるため、Accessは何らかの形でそれを満たす必要があります。これを行うには、CityName
のフルセットをダウンロードすることになります。 およびそれに対応するCityID
VBA関数に渡すためにMyUCase()
結果を評価します。その結果、Accessがより多くのデータを要求し、より多くの作業を行うようになったため、クエリの実行速度が大幅に低下しました。
UCase()
を使用しましたが この例では、一般的に、データソースからできるだけ多くの作業をリモート化する方がよいことがわかります。しかし、データソースのネイティブSQLダイアレクトに書き換えることができない複雑なVBA関数がある場合はどうなるでしょうか。このシナリオは非常にまれだと思いますが、検討する価値があります。フィルタを追加して、返される都市のセットを絞り込むことができると仮定しましょう。
SELECT c.CityID ,c.CityName ,c.StateProvinceID FROM Cities AS c WHERE c.CityName LIKE "Bos*" AND MyUCase([c].[CityName])="BOSTON";トレースされたODBCSQLは、次のようになります。
SQLExecDirect: SELECT "CityName" ,"c"."CityID" FROM "Application"."Cities" "c" WHERE ("CityName" LIKE 'Bos%' )アクセスは
LIKE
をリモートできます データソースに戻ると、はるかに小さいデータセットが返されます。 MyUCase()
のローカル評価は引き続き実行されます 結果のデータセット。返されるデータセットが小さいため、クエリの実行速度が大幅に向上します。 これは、クエリから複雑なVBA関数を簡単にリファクタリングできないという望ましくないシナリオに直面した場合でも、リモートで操作できるレコードの初期セットを減らすことができるフィルターを追加することで、悪影響を軽減できることを示しています。
保管可能性に関する注記
前の例では、列にスカラー関数を適用しました。これは、クエリを「非引数可能」としてレンダリングする可能性があります。これは、データベースエンジンが、インデックスを使用してクエリを最適化して一致を検索および検索できないことを意味します。 「sargability」という単語の「sarg」の部分は、「SearchARGument」を指します。テーブルのデータソースでインデックスが定義されているとします。
CREATE INDEX IX_Cities_CityName ON Application.Cities (CityName);
UCASE(CityName)
などの式 データベースエンジンがインデックスIX_Cities_CityName
を使用できないようにします これは、AccessがカスタムVBA関数で行ったように、エンジンが各行を1つずつ評価して一致を見つけるように強制されるためです。最近のバージョンのSQLServerなどの一部のデータベースエンジンは、式に基づくインデックスの作成をサポートしています。 UCASE()
を使用してクエリを最適化する場合 transact-SQL関数、インデックス定義を調整できます: CREATE INDEX IX_Cities_Boston_Uppercase ON Application.Cities (CityName) WHERE UCASE(CityName) = 'BOSTON';これにより、SQLServerはクエリを
WHERE UCase(CityName) = 'BOSTON'
で処理できるようになります。 インデックスIX_Cities_Boston_Uppercase
を使用できるようになったため、sargableクエリとして 一致するレコードを返します。ただし、クエリが'CLEVELAND'
で一致した場合 'BOSTON'
の代わりに 、sargabilityは失われます。 実際に使用しているデータベースエンジンに関係なく、パフォーマンスの問題を回避するために、可能な限り引数可能なクエリを設計して使用することをお勧めします。重要なクエリには、最高のパフォーマンスを提供するためのカバーインデックスが必要です。実際には引数不可能なクエリの設計を回避するために、引数可能性とカバーインデックスについてさらに学習することをお勧めします。
結論
AccessがAccessSQLからODBCクエリへのフィルターの適用をどのように処理するかを確認しました。また、Accessがさまざまなタイプの参照をパラメーターに変換し、AccessがODBCレイヤーの外部で評価を実行し、それらを入力として準備されたODBCステートメントに渡すことを可能にするさまざまなケースについても調査しました。また、通常は入力として列参照が含まれているために、パラメーター化できない場合に何が起こるかについても調べました。これは、SQLサーバーへの移行中のパフォーマンスに影響を与える可能性があります。
特定の関数では、Accessが式を変換して、代わりにODBCスカラー関数を使用できる場合があります。これにより、Accessは式をODBCデータソースにリモート接続できます。これの1つの影響は、スカラー関数の実装が異なる場合、クエリの動作が異なるか、実行速度が速くなる可能性があることです。 VBA関数は、他の方法ではリモート可能なスカラー関数をラップする単純な関数であっても、式をリモート化するための作業を打ち負かすことができることを確認しました。また、Accessクエリ/レコードソース/行ソースから複雑なVBA関数をリファクタリングできない状況では、クエリにフィルターを追加して量を減らすことで、少なくともコストのかかるダウンロードを軽減できることも学びます。返されたデータの数。
次の記事では、Accessによって結合がどのように処理されるかを見ていきます。
Microsoft Accessのヘルプをお探しですか?今日、773-809-5456で専門家に電話するか、[email protected]までメールでお問い合わせください。