Access
 sql >> Base de données >  >> RDS >> Access

Comment Access communique-t-il avec les sources de données ODBC ? Partie 6

Effet des jointures dans un jeu d'enregistrements

Dans notre sixième et dernier article de la série de traçage ODBC, nous allons examiner comment Access gérera les jointures dans les requêtes Access. Dans l'article précédent, vous avez vu comment les filtres sont gérés par Access. Selon l'expression, Access peut choisir de ne pas la paramétrer ou être obligé de l'évaluer lui-même en téléchargeant toutes les données d'entrée puis en effectuant les évaluations localement. Dans cet article, nous nous concentrerons sur les jointures. Quand on y pense, les jointures sont en fait un type particulier de filtre. Par conséquent, en théorie, Access devrait être distant autant que possible, même avec des jointures. Vous pouvez généralement voir des jointures écrites dans le pseudo-SQL suivant :

FROM a INNER JOIN b ON a.ID = b.ID
Cependant, elle peut être considérée comme équivalente à la syntaxe suivante :

FROM a, b WHERE a.ID = b.ID
Cela illustre que même si nous pouvons utiliser le JOIN..ON plus lisible et familier , l'accès est libre de le traiter comme un WHERE ce qui est utile dans les situations où Access ne peut pas entièrement distancer la requête. Mais voici le hic… quand Access décide-t-il de distancer les jointures ? Essayons une simple requête de jointure :

SELECT 
   c.CityID
  ,c.StateProvinceID
  ,c.CityName
  ,s.StateProvinceName
FROM Cities AS c 
INNER JOIN StateProvinces AS s 
  ON c.StateProvinceID = s.StateProvinceID;
Si nous traçons cette requête, nous verrons le résultat suivant :

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 a décidé de ne pas effectuer la jointure à distance, même si la requête Access d'origine peut parfaitement être exécutée sur SQL Server. Au lieu de cela, il a obtenu les ID de chaque table dans une jointure thêta, puis a configuré 2 chaînes de requêtes distinctes comme si nous avions ouvert 2 jeux d'enregistrements de type dynaset. Les deux requêtes préparées différentes reçoivent ensuite les clés des tables respectives de la première requête. Comme on pouvait s'y attendre, cela peut représenter beaucoup de bavardages sur le réseau.

Si nous modifions la même requête Access pour qu'elle soit de type instantané au lieu du type de dynaset par défaut, nous obtenons :

SQLExecDirect: 
SELECT 
   "c"."CityID"
  ,"c"."CityName"
  ,"c"."StateProvinceID"
  ,"s"."StateProvinceName"  
FROM "Application"."Cities" "c",
     "Application"."StateProvinces" "s" 
WHERE ("c"."StateProvinceID" = "s"."StateProvinceID" )
Access effectue donc très bien les jointures à distance dans le cas d'une requête de type instantané. Pourquoi Access n'a-t-il pas fait cela avec la requête de type dynaset d'origine ? L'indice est dans la capture d'écran suivante où nous essayons de modifier les deux colonnes des tableaux dans la capture d'écran suivante :

Une telle requête permet de mettre à jour les deux colonnes. Ce n'est pas réellement exprimable en SQL, mais une telle action est légale pour l'utilisateur. Par conséquent, pour exécuter cette mise à jour, Access soumettra le SQL ODBC suivant :

SQLExecDirect: 
UPDATE "Application"."StateProvinces" 
SET "StateProvinceName"=?  
WHERE "StateProvinceID" = ? 
  AND "StateProvinceName" = ?

SQLExecDirect: 
UPDATE "Application"."Cities" 
SET "CityName"=?  
WHERE "CityID" = ? 
  AND "CityName" = ? 
  AND "StateProvinceID" = ?
Cela ne serait pas possible si Access ne disposait pas des informations requises pour mettre à jour chaque table, ce qui explique pourquoi Access a choisi de ne pas effectuer la jointure à distance lors de la résolution de la requête de type dynaset d'origine. La leçon ici est que si vous n'avez pas besoin d'une requête pouvant être mise à jour et que les données résultantes sont suffisamment petites, il peut être préférable de convertir la requête en un type d'instantané. Dans le cas où vous devez formuler une source d'enregistrement complexe, vous obtiendrez généralement de bien meilleures performances en utilisant une vue SQL comme base plutôt qu'en effectuant les jointures côté accès.

Pour le prouver, nous allons créer une vue SQL et la lier à 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;
Nous ajustons ensuite la requête d'accès comme suit :

SELECT 
   c.CityID
  ,c.StateProvinceID
  ,c.CityName
  ,c.StateProvinceName
FROM vwCitiesAndStates AS c;
Si nous répétons ensuite la mise à jour que nous avons essayée à l'origine, nous devrions voir le SQL ODBC tracé suivant :

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" = ?
Cela démontre qu'en utilisant des vues SQL pour « éloigner » les jointures, Access ne fonctionnera qu'avec une seule source, plutôt qu'avec 2 tables et à distance la mise à jour de la vue entièrement vers SQL Server. Un effet secondaire est que cette mise à jour échouera désormais avec le message d'erreur :

Cela ne devrait pas être une surprise puisque nous faisions une UPDATE sur une seule source alors que dans l'exemple d'origine, Access en émettait secrètement deux UPDATE séparé déclarations sur chaque table individuelle. J'espère que cela aide à démontrer que vous devez éviter de faire des jointures dans les requêtes Access/recordsources/rowsources, en particulier lorsqu'elles doivent être mises à jour. Si ce n'est pas le cas, utilisez un instantané dans la mesure du possible.

Une note rapide concernant les jointures hétérogènes

Nous devons commenter les jointures entre deux tables liées provenant de deux sources de données ODBC différentes. De telles jointures sont "hétérogènes" car Access doit gérer les jointures localement puisque chaque source de données est supposée ne pas se connaître. Que vous spécifiiez un jeu d'enregistrements de type feuille de réponse dynamique ou de type instantané, Access doit récupérer le jeu complet de clés de chaque source de données et résoudre les jointures en envoyant des requêtes paramétrées distinctes à chaque source de données. Si la mise à jour est autorisée, Access formulera un UPDATE séparé requête à chaque source de données qui doit être mise à jour. Il est également important de noter qu'une jointure entre deux tables liées provenant chacune de deux bases de données différentes est toujours considérée par Access comme hétérogène. Cela reste vrai même si les deux bases de données se trouvent sur le même serveur et que vous n'avez aucun problème à effectuer des requêtes croisées. Dans ce scénario, une vue SQL peut aider à réduire le bavardage supplémentaire en masquant les jointures entre bases de données d'Access, comme nous l'avons déjà vu dans cet article.

Différence de syntaxe de jointure externe

Tant que les jointures externes n'affectent pas la capacité de mise à jour de la requête Access, Access la gère de la même manière qu'il a géré la version de jointure interne. Si nous modifions la même requête que nous avions l'habitude d'être une jointure gauche, le SQL ODBC tracé affichera la requête de population de clé comme suit :

SQLExecDirect: 
SELECT 
   "c"."CityID"
  ,"s"."StateProvinceID" 
FROM {oj 
	"Application"."Cities" "c" 
	LEFT OUTER JOIN "Application"."StateProvinces" "s" 
		ON ("c"."StateProvinceID" = "s"."StateProvinceID" ) 
}
La syntaxe est assez différente de ce à quoi vous pourriez vous attendre dans d'autres dialectes SQL. En effet, la grammaire SQL ODBC nécessite que toutes les jointures externes soient enveloppées dans un {oj ...} expression. Pour plus de détails sur cette syntaxe, consultez la documentation. Pour notre propos, nous pouvons simplement ignorer le {oj et le } de fermeture comme du bruit.

Conclusion

Nous avons vu que les jointures sont traitées comme s'il s'agissait d'une sorte de filtre et qu'Access essaiera de déplacer les jointures là où il est autorisé. Un domaine particulier auquel il faut prêter une attention particulière est le fait que, par défaut, nous utilisons des jeux d'enregistrements de type dynaset et Access ne fera aucune hypothèse quant à savoir si nous voulons autoriser la modification de telle ou telle colonne dans le jeu d'enregistrements et fait tout son possible pour nous pour mettre à jour deux tables, ce qui n'est en fait pas facile à exprimer en SQL standard. Par conséquent, Access fera beaucoup plus de travail pour prendre en charge la possibilité de mise à jour d'une requête contenant des jointures pouvant avoir un impact négatif sur les performances.

Nous pouvons aider à éviter la pénalité en utilisant des vues SQL à la place des jointures exprimées dans une requête Access. Le compromis est que nous sommes alors soumis aux règles de mise à jour d'une vue SQL ; nous pourrions ne pas être autorisés à mettre à jour deux tables en même temps. Habituellement, parce qu'un formulaire Access bien conçu ne représentera qu'une seule table à mettre à jour, ce n'est pas vraiment une restriction et c'est une bonne discipline à suivre.

Avec cela, la série actuelle est terminée. Cependant, l'apprentissage que la série suscitera, espérons-le, ne devrait pas être fait. J'espère sincèrement que vous avez trouvé la série utile et j'ai hâte d'entendre les nouvelles informations que vous avez acquises grâce à l'utilisation d'outils pour aider à analyser et à résoudre les problèmes de performances avec les applications Access utilisant des sources de données ODBC. N'hésitez pas à laisser des commentaires ou à demander plus d'informations et merci d'avoir lu ensemble !

Pour plus d'assistance sur tout ce qui concerne Microsoft Access, appelez nos experts au 773-809-5456 ou envoyez-nous un e-mail à [email protected].