IX. Mise en forme des données▲
Le moteur de curseur client implémente un fournisseur de service qui permet de construire des recordset un peu particuliers, le « Data Shaping Service for OLE Db ». Cette mise en forme des données peut souvent ressembler à une requête de jointure standard ou à l'utilisation d'agrégats, mais le résultat final est sensiblement différent. Je ne vais pas visiter en détail ici le langage Shape, car sa syntaxe peut devenir rapidement complexe, aussi n'allons nous voir que quelques exemples. Toutefois dans le deuxième exemple nous verrons une technique de création de requêtes.
N.B : On peut utiliser le composant DataEnvironment pour générer des requêtes Shape.
Pour faire simple, disons qu'un recordset « Shape » est un recordset auquel on a ajouté un (des) champ(s), ce champ pouvant contenir une référence à un autre recordset, une valeur calculée sur la ligne ou sur une colonne d'un recordset, etc.
Cette mise en forme est faite par le langage Shape dont nous allons voir ici deux cas d'utilisation.
Tout d'abord, il faut spécifier deux fournisseurs à la connexion, le fournisseur de service dans la propriété Provider de la connexion, et le fournisseur de données dans la propriété dynamique « Data Provider » de l'objet Connection. Évidemment, on peut aussi passer les informations dans la chaîne de connexion. Ensuite l'astuce principale consiste à toujours travailler vis-à-vis du Recordset enfant (ou secondaire) pour construire le recordset.
IX-A. Recordset hiérarchique▲
Dans cet exemple nous allons construire un recordset hiérarchique. Ce recordset contiendra la liste de tous les auteurs, chacun de ces enregistrements contiendra la liste de tous les livres qu'ils ont écrits.
Pour affecter un recordset enfant à un recordset parent, on doit utiliser la commande Shape APPEND et préciser une clause RELATE qui permet de faire la relation entre le parent et l'enfant. Bien sûr, la hiérarchie va dépendre de la structure de la base. Dans le cas de la base « biblio » je vais devoir utiliser trois tables pour construire mon recordset. Regardons le code suivant :
Dim
cnn1 As
ADODB.Connection
, MonRs As
ADODB.Recordset
, RsLect As
ADODB.Recordset
Set
cnn1 =
New
ADODB.Connection
cnn1.CursorLocation
=
adUseClient
cnn1.Provider
=
"MSDataShape"
cnn1.Properties
(
"Data Provider"
) =
"Microsoft.Jet.OLEDB.4.0"
cnn1.Open
"Data Source=d:\biblio.mdb"
'autre méthode d'ouverture possible
'cnn1.open "Provider = MSDataShape;Data Provider=Microsoft.Jet.OLEDB.4.0
';Data Source=d:\biblio.mdb ;User Id=Admin; Password="
Set
MonRs =
New
ADODB.Recordset
MonRs.StayInSync
=
False
MonRs.Open
"SHAPE {SELECT * FROM `Authors`} AS cmdMere APPEND (( SHAPE {SELECT * FROM `Title Author`}
AS
cmdFille APPEND (
{SELECT
*
FROM `Titles`}
AS
cmdPetFille RELATE 'ISBN' TO 'ISBN')
AS
cmdPetFille) AS
cmdFille RELATE 'Au_ID' TO 'Au_ID') AS cmdFille", cnn1
'possibilité d'utiliser une grille hierarchique pour visualiser le résultat
'Set MSHFlexGrid1.DataSource = MonRs
Set
RsLect =
New
ADODB.Recordset
Set
RsLect =
MonRs
(
"cmdFille"
).Value
Set
RsLect =
RsLect
(
"cmdPetFille"
).Value
Dans ce cas je crée une relation composée avec ma commande Shape, ce qui revient à dire que mon recordset est composé de trois Recordset. Pour pouvoir lire les données de la table « Titles », je crée un recordset pour aller récupérer le recordset renvoyé par l'alias « cmdPetFille ». Comme vous le voyez, rien de bien sorcier.
IX-B. Agrégat▲
Les agrégats réalisés par la commande SHAPE sont sensiblement les mêmes que ceux réalisés à l'aide du SQL.
Dans cet exemple nous allons voir comment construire sans se tromper une commande Shape. L'objectif est de récupérer le nom de l'auteur et le nombre de livres qu'il a écrits. Ceci peut ressembler à l'exécution de la requête SQL suivante :
SELECT
Authors.Author, Count
(
[Title Author]
.ISBN)
AS
NbLivre
FROM
Authors
INNER
JOIN
[Title Author]
ON
Authors.Au_ID =
[Title Author]
.Au_ID
GROUP
BY
Authors.Author;
Mais il y a une différence. Avec ma commande Shape, je garde la possibilité d'accéder à la valeur du champ 'ISBN' ce que ne me permet pas la requête ci-dessus. Nous allons utiliser le code suivant :
Dim
cnn1 As
ADODB.Connection
, MonRs As
ADODB.Recordset
, RsLect As
ADODB.Recordset
Set
cnn1 =
New
ADODB.Connection
cnn1.open
"Provider = MSDataShape;Data Provider=Microsoft.Jet.OLEDB.4.0;Data Source=d:\biblio.mdb ;User Id=Admin; Password="
Set
MonRs =
New
ADODB.Recordset
MonRs.StayInSync
=
False
MonRs.Open
" SHAPE {SELECT * FROM `Authors`}
AS
cmdAuthor APPEND ((
SHAPE {SELECT
*
FROM `Title Author`}
AS
cmdFille COMPUTE cmdFille, COUNT
(
cmdFille.
'ISBN')
AS
AgrISBN BY 'Au_ID') AS cmdGrISBN RELATE 'Au_ID' TO 'Au_ID') AS cmdGrISBN", cnn1
Set
RsLect =
New
ADODB.Recordset
Set
RsLect =
MonRs
(
"cmdGrISBN"
).Value
'affiche la valeur du compte de livre du premier enregistrement
Debug.Print
RsLect!AgrISBN
Set
RsLect =
RsLect
(
"cmdFille"
).Value
'affiche la valeur ISBN du premier enregistrement
Debug.Print
RsLect!ISBN
Le code n'a rien de bien complexe aussi ne vais-je pas entrer dans le détail, par contre nous allons étudier la commande SHAPE.
Pour mieux comprendre regardons là écrite ainsi :
SHAPE {SELECT * FROM `Authors`} AS cmdAuthor APPEND ( ( SHAPE {SELECT * FROM `Title Author`} AS cmdFille COMPUTE cmdFille, COUNT(cmdFille.'ISBN') AS AgrISBN BY 'Au_ID') AS cmdGrISBN RELATE 'Au_ID' TO 'Au_ID') AS cmdGrISBN
Les règles de base sont la suivante
SHAPE est toujours lié avec un APPEND ou un COMPUTE. Il devra donc y avoir autant de fois SHAPE dans la commande que l'on trouvera de fois les termes Append et COMPUTE.
Chaque commande SHAPE ajoute une colonne au recordset dans lequel il est imbriqué
Les champs des relations doivent être présents dans la commande mère et la commande fille.
Je vous ai dit au début du chapitre de raisonner plutôt du côté fils. Dans ce cas, la construction se fait de la manière suivante.
- Le but est d'obtenir le compte de ISBN, je construis donc l'agrégat de la forme COMPUTE Recordset1, Agrégat (Champs de l'agrégat) AS alias BY regroupement éventuel. Dans notre cas la commande en vert ou recordset1 est cmdFille.
- Mon recordset1 doit contenir le champ utilisé dans l'agrégat (dans notre cas 'ISBN'). Pour nous le recordset vient de la table 'Title Author'. Mon recordset1 est donc une commande SHAPE de la forme SHAPE Requête As Alias COMPUTE Agrégat AS Alias final. Notez que cette commande serait un recordset mis en forme valide si je ne souhaitais pas l'imbriquer plus avant.
- Mon Alias final doit contenir le champ à mettre en relation avec le recordset parent. Je n'ai pas besoin stricto sensu de cet alias intermédiaire, sauf pour des besoins de lecture. Je vais donc réutiliser le même comme alias dans le recordset parent. Je vais donc avoir SHAPE (recordset parent) AS Alias (facultatif) APPEND (Recordset imbriqué) AS Alias (facultatif) RELATE relation AS Alias
Comme je le vois j'utilise deux alias facultatifs et la commande : SHAPE {SELECT * FROM `Authors`} APPEND (( SHAPE {SELECT * FROM `Title Author`} AS cmdFilleCOMPUTE cmdFille, COUNT(cmdFille.'ISBN') AS AgrISBN BY 'Au_ID' ) RELATE 'Au_ID' TO 'Au_ID') AS cmdGrISBN
Et strictement équivalente. Pour expliciter mieux ce que nous obtenons, visualisons la structure.
Mon recordset final est donc composé de trois recordset.