Bonjour à tous,
J'ai un petit problème à vous soumettre à propos des manipulations de l'objet recordset.
Je replace d'abord le contexte.
Je suis en train de basculer la gestion de portefeuilles boursiers d'Excel vers Access qui, compte tenu de la complexité accrue du sujet (plusieurs comptes titres dans plusieurs banques différentes, plusieurs porteurs et donc un historique de cours qui commence à devenir lourd).
Ma base de données est organisée de la façon suivante pour l'historique des cours et le nombre de titres présent sur chaque compte. Chaque titre est détaillé dans une table dont le nom est le code ISIN (identifiant international unique pour toute valeur cotée en bourse).
Chaque enregistrement de ces tables représente les données à une date :
- champ "DateCours" (format date), unique et indexé en tant que clé primaire de nom "PK_DateCours" ;
- champ "Cours" (format numérique double), non indexé, qui contient le cours de la valeur à la date donnée ;
- autant de champs que nécessaire dont les noms sont "CPxxxx" (format numérique entier long) où les deux premiers "x" reprennent l'id du numéro du compte qui porte cette valeur et les deux derniers, l'id du porteur qui détient cette valeur. Pour info les id cités proviennent des tables "Comptes" et "Portefeuilles" qui décrivent leurs caractéristiques. Ces champs "CPxxxx" contiennent le nombre de titres détenus à la date donnée.
Je mets donc à jour ces tables à partir des feuilles Excel qui contenaient les données.
La première étape, qui s'est bien déroulée, a été la création de ces tables titres avec l'historique des cours (remplissage des champs "DateCours" et "Cours").
J'ai donc environ 150 de ces tables dans la base.
Lors de l'étape suivante, je crée dans ces tables le champ "CP0101" qui contiendra le nombre de titres détenus par le portefeuille 01 dans le compte 01.
Pour ce faire, sous Excel, j'ai créé un fichier csv par table contenant deux champs par enregistrement reprenant chacun la date et le nombre de titres détenus à cette date et j'intègre ces données dans chaque table correspondante.
La macro est la suivante :
Première question : pourquoi est-on obligé de passer par une table temporaire pour récupérer les données du fichier csv ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88 Sub ImportNbrCSV() ' Déclaration des variables Dim dtbBase As DAO.Database Dim tblTable As DAO.TableDef Dim fldChamp As DAO.Field Dim rstEnregO, rstEnregD As DAO.Recordset Dim strClasseurNom, strFicNom, strTableNom, strTableTmp, strChampNom, strRequete1, strRequete2 As String Dim sngChrono As Single ' Définition des variables Set dtbBase = CurrentDb strClasseurNom = "C:\Bourse\" strFicNom = Dir(strClasseurNom & "*.csv", vbDirectory) strTableTmp = "TableTmp" strChampNom = "CP0101" ' Boucle sur les fichiers CSV Do While (strFicNom <> "") ' Teste si c'est un fichier ou un répertoire If (GetAttr(strClasseurNom & strFicNom) And vbDirectory) <> vbDirectory Then ' C'est un fichier, on en déduit le nom de la table concernée à partir du nom du fichier strTableNom = Left(strFicNom, Len(strFicNom) - 4) ' Ajout d'un champ dans la table destination Set tblTable = dtbBase.TableDefs(strTableNom) Set fldChamp = tblTable.CreateField(strChampNom, dbLong) fldChamp.Required = True tblTable.Fields.Append fldChamp Set fldChamp = Nothing tblTable.Fields.Refresh ' Transfert du fichier trouvé dans une table temporaire avec le modèle d'import adéquat DoCmd.SetWarnings False DoCmd.TransferText acLinkDelim, "Import_CodeISIN", strTableTmp, strClasseurNom & strFicNom ' Ajout des données dans la table de destination Set rstEnregO = dtbBase.OpenRecordset(strTableTmp) Set rstEnregD = dtbBase.OpenRecordset(strTableNom) ' Boucle sur les enregistrements de la table temporaire sngChrono = Timer While Not rstEnregO.EOF ' Met la table destinataire à jour à partir de l'enregistrement de la table temporaire ' Solution 1 : RunSQL strRequete1 = "UPDATE " & strTableNom & _ " SET " & strTableNom & "." & strChampNom & " = " & CLng(rstEnregO.Fields("Cours").Value) & _ " WHERE " & strTableNom & ".DateCours = #" & Format(rstEnregO.Fields("DateCours").Value, "mm\/dd\/yyyy") & "#" DoCmd.RunSQL strRequete1 ' Solution 2 : FindFirst 'strRequete1 = "DateCours=#" & Format(rstEnregO.Fields("DateCours").Value, "mm\/dd\/yyyy") & "#" 'rstEnregD.FindFirst strRequete1 'rstEnregD.Edit 'rstEnregD.Fields(strChampNom).Value = CLng(rstEnregO.Fields("Cours").Value) 'rstEnregD.Update ' Solution 3 : Seek 'strRequete1 = "#" & Format(rstEnregO.Fields("DateCours").Value, "mm\/dd\/yyyy") & "#" 'strRequete2 = Format(rstEnregO.Fields("DateCours").Value, "mm\/dd\/yyyy") 'rstEnregD.Index = "PK_DateCours" 'rstEnregD.Seek "=", strRequete1 'rstEnregD.Seek "=", strRequete2 'rstEnregD.Edit 'rstEnregD.Fields(strChampNom).Value = CLng(rstEnregO.Fields("Cours").Value) 'rstEnregD.Update rstEnregO.MoveNext DoEvents Wend MsgBox "Durée du traitement : " & Timer - sngChrono & " secondes" ' Efface la table temporaire et le fichier csv Set rstEnregO = Nothing Set rstEnregD = Nothing Set tblTable = Nothing DoCmd.DeleteObject acTable, strTableTmp Kill strClasseurNom & strFicNom DoCmd.SetWarnings True End If ' Passe au fichier csv suivant strFicNom = Dir DoEvents Loop Set dtbBase = Nothing End Sub
J'ai bien sûr essayé d'enregistrer les données directement dans la table destinataire, mais ça ne marche pas...
Ensuite, pour mettre à jour la table destinataire, je parcours la table temporaire enregistrement par enregistrement après avoir créé l'objet recordset "rstEnregO".
Là, je recherche l'enregistrement de la table destinataire dont la date correspond à celle de l'enregistrement de la table temporaire pour mettre à jour le champ "CPxxxx" avec la donnée contenue dans le second champ de l'enregistrement de la table temporaire.
J'ai d'abord exécuté cette opération par la méthode RunSQL (Solution 1) qui fonctionne parfaitement. Mais je me suis demandé si c'était la plus rapide.
Donc j'ai tenté tour à tour les solutions 2 et 3 qui font appel à l'objet recordset "rstEnregD" créé sur la table destinataire.
La solution 2 avec la méthode FindFirst génère l'erreur 3251 (Opération non autorisée pour ce type d'objet) sur la commande "rstEnregD.FindFirst strRequete1".
La solution 3 avec la méthode Seek génère l'erreur 3421 (Erreur de conversion de typede données) pour le critère de recherche strRequete1 (avec dièses entourant la date) sur la commande "rstEnregD.Seek "=", strRequete1" et l'erreur 3021 (Aucun enregistrement en cours) avec le critère strRequete2 (sans les dièses) sur la commande "rstEnregD.Edit", ce qui veut dire qu'il n'a pas trouvé l'enregistrement cherché.
Je ne comprends pas pourquoi ces erreurs sont générées. C'est donc l'objet de ma seconde question.
Merci d'avance.
Partager