Auswirkung von Joins in einem Recordset
In unserem sechsten und letzten Artikel der ODBC-Ablaufverfolgungsserie werden wir einen Blick darauf werfen, wie Access Verknüpfungen in Access-Abfragen handhabt. Im vorherigen Artikel haben Sie gesehen, wie Filter von Access gehandhabt werden. Abhängig vom Ausdruck kann Access entscheiden, ihn wegzuparametrieren, oder gezwungen sein, ihn selbst auszuwerten, indem alle Eingabedaten heruntergeladen und die Auswertungen dann lokal durchgeführt werden. In diesem Artikel konzentrieren wir uns auf Joins. Wenn Sie darüber nachdenken, sind Verknüpfungen eigentlich eine besondere Art von Filter. Daher sollte Access theoretisch auch bei Joins so viel wie möglich remote machen. Typischerweise sehen Sie Joins, die in folgendem Pseudo-SQL geschrieben sind:
FROM a INNER JOIN b ON a.ID = b.IDEs kann jedoch als äquivalent zu der folgenden Syntax betrachtet werden:
FROM a, b WHERE a.ID = b.IDDas verdeutlicht das, auch wenn wir vielleicht den besser lesbaren und vertrauteren
JOIN..ON
verwenden , Access steht es frei, es als WHERE
zu behandeln Dies ist in Situationen hilfreich, in denen Access die Abfrage nicht vollständig remoten kann. Aber hier ist der Haken … wann entscheidet Access, die Joins aus der Ferne zu entfernen? Versuchen wir es mit einer einfachen Join-Abfrage: SELECT c.CityID ,c.StateProvinceID ,c.CityName ,s.StateProvinceName FROM Cities AS c INNER JOIN StateProvinces AS s ON c.StateProvinceID = s.StateProvinceID;Wenn wir diese Abfrage verfolgen, sehen wir die folgende Ausgabe:
SQLExecDirect: SELECT "c"."CityID" ,"s"."StateProvinceID" FROM "Application"."Cities" "c", "Application"."StateProvinces" "s" WHERE ("c"."StateProvinceID" = "s"."StateProvinceID" ) SQLPrepare: SELECT "CityID" ,"CityName" ,"StateProvinceID" FROM "Application"."Cities" WHERE "CityID" = ? SQLExecute: (GOTO BOOKMARK) SQLPrepare: SELECT "StateProvinceID" ,"StateProvinceName" FROM "Application"."StateProvinces" WHERE "StateProvinceID" = ? SQLExecute: (GOTO BOOKMARK) SQLPrepare: SELECT "StateProvinceID" ,"StateProvinceName" FROM "Application"."StateProvinces" WHERE "StateProvinceID" = ? OR "StateProvinceID" = ? OR "StateProvinceID" = ? OR "StateProvinceID" = ? OR "StateProvinceID" = ? OR "StateProvinceID" = ? OR "StateProvinceID" = ? OR "StateProvinceID" = ? OR "StateProvinceID" = ? OR "StateProvinceID" = ? SQLExecute: (MULTI-ROW FETCH) SQLPrepare: SELECT "CityID" ,"CityName" ,"StateProvinceID" FROM "Application"."Cities" WHERE "CityID" = ? OR "CityID" = ? OR "CityID" = ? OR "CityID" = ? OR "CityID" = ? OR "CityID" = ? OR "CityID" = ? OR "CityID" = ? OR "CityID" = ? OR "CityID" = ? SQLExecute: (MULTI-ROW FETCH) SQLExecute: (MULTI-ROW FETCH) SQLExecute: (MULTI-ROW FETCH) SQLExecute: (MULTI-ROW FETCH) SQLExecute: (MULTI-ROW FETCH) SQLExecute: (MULTI-ROW FETCH) SQLExecute: (MULTI-ROW FETCH) SQLExecute: (MULTI-ROW FETCH) SQLExecute: (MULTI-ROW FETCH)Access hat entschieden, den Join nicht remote auszuführen, obwohl die ursprüngliche Access-Abfrage durchaus in der Lage ist, auf SQL Server ausgeführt zu werden. Stattdessen hat es die IDs aus jeder Tabelle in einem Theta-Join abgerufen und dann zwei separate Abfrageketten eingerichtet, als ob wir zwei Recordsets vom Dynaset-Typ geöffnet hätten. Die beiden unterschiedlich vorbereiteten Abfragen werden dann mit den Schlüsseln für die jeweiligen Tabellen aus der ersten Abfrage gefüttert. Wie vorhersehbar, kann das eine Menge Geschwätz sein, wenn es über das Netzwerk geht.
Wenn wir dieselbe Access-Abfrage in einen Snapshot-Typ anstelle des Standard-Dynaset-Typs ändern, erhalten wir:
SQLExecDirect: SELECT "c"."CityID" ,"c"."CityName" ,"c"."StateProvinceID" ,"s"."StateProvinceName" FROM "Application"."Cities" "c", "Application"."StateProvinces" "s" WHERE ("c"."StateProvinceID" = "s"."StateProvinceID" )Access entfernt also die Verknüpfungen im Falle einer Snapshot-Abfrage problemlos. Warum hat Access das nicht mit der ursprünglichen Dynaset-Typ-Abfrage gemacht? Der Hinweis ist im folgenden Screenshot, wo wir versuchen, beide zu bearbeiten Tabellenspalten im folgenden Screenshot:
Eine solche Abfrage ermöglicht die Aktualisierung beider Spalten. Dies ist in SQL nicht wirklich ausdrückbar, aber eine solche Aktion ist für den Benutzer legal. Um diese Aktualisierung auszuführen, würde Access daher das folgende ODBC-SQL übermitteln:
SQLExecDirect: UPDATE "Application"."StateProvinces" SET "StateProvinceName"=? WHERE "StateProvinceID" = ? AND "StateProvinceName" = ? SQLExecDirect: UPDATE "Application"."Cities" SET "CityName"=? WHERE "CityID" = ? AND "CityName" = ? AND "StateProvinceID" = ?Dies wäre nicht möglich, wenn Access nicht über die Informationen verfügte, die zum Aktualisieren jeder Tabelle erforderlich sind, was erklärt, warum Access beim Auflösen der ursprünglichen Dynaset-Typ-Abfrage entschieden hat, die Verknüpfung nicht remote durchzuführen. Die Lektion hier ist, dass es besser sein könnte, die Abfrage in einen Snapshot-Typ zu konvertieren, wenn Sie keine aktualisierbare Abfrage benötigen und die resultierenden Daten klein genug sind. In dem Fall, in dem Sie eine komplexe Datensatzquelle formulieren müssen, erhalten Sie normalerweise eine viel bessere Leistung, wenn Sie eine SQL-Ansicht als Basis verwenden, als die Verknüpfungen auf der Zugriffsseite durchzuführen.
Um dies zu beweisen, erstellen wir eine SQL-Ansicht und verknüpfen sie mit Access:
CREATE VIEW dbo.vwCitiesAndStates AS SELECT c.CityID ,c.StateProvinceID ,c.CityName ,s.StateProvinceName FROM Application.Cities AS c INNER JOIN Application.StateProvinces AS s ON c.StateProvinceID = s.StateProvinceID;Anschließend passen wir die Access-Abfrage wie folgt an:
SELECT c.CityID ,c.StateProvinceID ,c.CityName ,c.StateProvinceName FROM vwCitiesAndStates AS c;Wenn wir dann das ursprünglich versuchte Update wiederholen, sollten wir das folgende nachverfolgte ODBC-SQL sehen:
SQLExecDirect: SELECT "c"."CityID" FROM "dbo"."vwCitiesAndStates" "c" SQLPrepare: SELECT "CityID" ,"StateProvinceID" ,"CityName" ,"StateProvinceName" FROM "dbo"."vwCitiesAndStates" WHERE "CityID" = ? SQLExecute: (GOTO BOOKMARK) SQLPrepare: SELECT "CityID" ,"StateProvinceID" ,"CityName" ,"StateProvinceName" FROM "dbo"."vwCitiesAndStates" WHERE "CityID" = ? OR "CityID" = ? OR "CityID" = ? OR "CityID" = ? OR "CityID" = ? OR "CityID" = ? OR "CityID" = ? OR "CityID" = ? OR "CityID" = ? OR "CityID" = ? SQLExecute: (MULTI-ROW FETCH) SQLExecute: (MULTI-ROW FETCH) SQLExecute: (MULTI-ROW FETCH) SQLExecute: (MULTI-ROW FETCH) SQLExecute: (MULTI-ROW FETCH) SQLExecute: (GOTO BOOKMARK) SQLExecDirect: UPDATE "dbo"."vwCitiesAndStates" SET "CityName"=?, "StateProvinceName"=? WHERE "CityID" = ? AND "StateProvinceID" = ? AND "CityName" = ? AND "StateProvinceName" = ?Dies zeigt, dass Access bei der Verwendung von SQL-Ansichten zum „Remote“ der Verknüpfungen nur mit einer einzigen Quelle funktioniert und nicht mit 2 Tabellen und die Aktualisierung der Ansicht vollständig an SQL Server entfernt. Ein Nebeneffekt ist, dass dieses Update nun mit der Fehlermeldung fehlschlägt:
Das sollte nicht überraschen, da wir ein UPDATE
durchgeführt haben auf einer einzelnen Quelle, während Access im ursprünglichen Beispiel tatsächlich zwei heimlich ausgab separates UPDATE
Angaben zu jeder einzelnen Tabelle. Hoffentlich trägt dies dazu bei, dass Sie Joins in Access-Abfragen/Recordsources/Rowsources vermeiden sollten, insbesondere wenn sie aktualisierbar sein müssen. Wenn dies nicht der Fall ist, verwenden Sie nach Möglichkeit Snapshots.
Ein kurzer Hinweis zu heterogenen Joins
Wir müssen uns zu Joins zwischen zwei verknüpften Tabellen äußern, die aus zwei verschiedenen ODBC-Datenquellen stammen. Solche Verknüpfungen sind „heterogen“, da Access die Verknüpfungen lokal handhaben muss, da davon ausgegangen wird, dass die einzelnen Datenquellen nichts voneinander wissen. Unabhängig davon, ob Sie Recordsets vom Dynaset-Typ oder Snapshot-Typ angeben, muss Access den vollständigen Satz von Schlüsseln aus jeder Datenquelle abrufen und die Joins auflösen, indem separate parametrisierte Abfragen an jede Datenquelle gesendet werden. Wenn die Aktualisierung erlaubt ist, formuliert Access ein separates UPDATE
Abfrage an jede Datenquelle, die aktualisiert werden muss. Es ist auch wichtig zu beachten, dass ein Join zwischen zwei verknüpften Tabellen, die aus jeweils zwei verschiedenen Datenbanken stammen, von Access immer noch als heterogen betrachtet wird. Dies gilt auch dann, wenn sich die beiden Datenbanken auf demselben Server befinden und Sie problemlos datenbankübergreifende Abfragen durchführen können. In diesem Szenario kann eine SQL-Ansicht dazu beitragen, das zusätzliche Geschwätz zu reduzieren, indem die datenbankübergreifenden Verknüpfungen vor Access ausgeblendet werden, ähnlich wie wir es bereits in diesem Artikel gesehen haben.
Äußerer Join-Syntaxunterschied
Solange sich die Outer Joins nicht auf die Aktualisierbarkeit der Access-Abfrage auswirken, behandelt Access sie ähnlich wie die Inner Join-Version. Wenn wir dieselbe Abfrage ändern, die wir früher als Linksverknüpfung verwendet haben, gibt das verfolgte ODBC-SQL die Schlüsselauffüllungsabfrage wie folgt aus:
SQLExecDirect: SELECT "c"."CityID" ,"s"."StateProvinceID" FROM {oj "Application"."Cities" "c" LEFT OUTER JOIN "Application"."StateProvinces" "s" ON ("c"."StateProvinceID" = "s"."StateProvinceID" ) }Die Syntax sieht ganz anders aus, als Sie es in anderen SQL-Dialekten erwarten würden. Das liegt daran, dass die ODBC-SQL-Grammatik erfordert, dass alle äußeren Verknüpfungen in einen
{oj ...}
eingeschlossen werden Ausdruck. Weitere Einzelheiten zu dieser Syntax finden Sie in der Dokumentation. Für unseren Zweck können wir den {oj
einfach ignorieren und der schließende }
als Rauschen. Schlussfolgerungen
Wir haben gesehen, dass Joins so behandelt werden, als ob sie eine Art Filter wären, und Access versucht, die Joins zu entfernen, wo dies zulässig ist. Ein besonderer Bereich, auf den Sie besonders achten sollten, ist die Tatsache, dass wir standardmäßig Recordsets vom Typ Dynaset verwenden und Access keine Annahmen darüber trifft, ob wir das Ändern von so und so-Spalten im Recordset zulassen möchten, und sich alle Mühe gibt, dies für uns zu ermöglichen in zwei Tabellen zu aktualisieren, was eigentlich nicht einfach in Standard-SQL ausgedrückt werden kann. Infolgedessen wird Access viel mehr Arbeit leisten, um die Aktualisierbarkeit für eine Abfrage zu unterstützen, die Verknüpfungen enthält, die sich negativ auf die Leistung auswirken können.
Wir können helfen, die Strafe zu vermeiden, indem wir SQL-Ansichten anstelle von Joins verwenden, die in einer Access-Abfrage ausgedrückt werden. Der Kompromiss besteht darin, dass wir dann den Aktualisierbarkeitsregeln einer SQL-Ansicht unterliegen; Wir dürfen möglicherweise nicht zwei Tabellen gleichzeitig aktualisieren. Da ein gut gestaltetes Access-Formular normalerweise nur eine einzige zu aktualisierende Tabelle darstellt, stellt dies keine große Einschränkung dar und ist eine gute Disziplin, die zu befolgen ist.
Damit ist die aktuelle Serie fertig. Die Erkenntnis, dass die Serie hoffentlich Funken schlägt, sollte jedoch nicht gemacht werden. Ich hoffe aufrichtig, dass Sie die Serie nützlich fanden, und freue mich darauf, von neuen Erkenntnissen zu hören, die Sie durch die Verwendung von Tools zur Analyse und Behebung von Leistungsproblemen bei Access-Anwendungen mithilfe von ODBC-Datenquellen gewonnen haben. Fühlen Sie sich frei, Kommentare zu hinterlassen oder weitere Informationen anzufordern und danke für das gemeinsame Lesen!
Für weitere Unterstützung in Bezug auf Microsoft Access rufen Sie unsere Experten unter 773-809-5456 an oder senden Sie uns eine E-Mail an [email protected].