Je suis actuellement en stage et je recherche à extraire les données csv de plusieurs fichiers, répartis par année dans 4 tables: lieux, caractéristiques, usagers et vehicules (les données sont disponibles sur ce site: https://www.data.gouv.fr/fr/datasets/ba ... 05-a-2022/). Je sais qu'il existe une extension pour cela, mais je veux que cela soit fait automatiquement et que plusieurs fichiers viennent compléter une même table, de plus les séparateur de texte varient en fonction des tables, les noms des champs varient également tout comme leurs ordre.
Je suis parvenu à réaliser des macros en BASIC permettant d'importer les fichiers CSV mais la vitesse de traitement est trop lente. Voici les enjeux principaux:
- Lire rapidement le fichier: existe-t-il une manière plus rapide qu'une autre pour lire et traiter un fichier texte
- Les champs peuvent contenir des séparateurs mais dans ce cas le champs est entre guillemets, j'utilise alors une boucle while pour chaque caractère et plusieurs appels de la fonction MID(), est-ce qu'il y a une alternative plus rapide que la fonction MID() pour ce cas précis?
- La fonction traitement_csv parcours tous les caractères de chaque ligne, donc je n'utilise pas la fonction replace()
Je ne vois pas comment optimiser les performances de mes macro. Voici mon code:
Code : Tout sélectionner
REM ***** BASIC *****
Option Explicit
Sub Ajout_temporaire_caracteristiques
importer_csv_dans_table("caracteristiques")
End Sub
Sub Ajout_temporaire_usagers
importer_csv_dans_table("usagers")
End Sub
Sub Ajout_temporaire_lieux
importer_csv_dans_table("lieux")
End Sub
Sub Ajout_temporaire_vehicules
importer_csv_dans_table("vehicules")
End Sub
Sub importer_csv_dans_table(table as String)
dim lien as string 'Lien du fichier CSV à importer
dim annee as integer 'Année du fichier
dim collection_lien as New Collection
dim i as Integer
dim j as Integer
dim Fenetre_chargement as object
dim nb_lignes_faites as Long
dim nb_lignes_totales as Long
dim maConnexion as Object
dim sRequete as Object
dim iNumFichier as Variant
dim sNomFichier as String
dim sLigne as String
dim aTableau as Variant
dim choix_auto_separateur as integer
dim debut as Long
DialogLibraries.LoadLibrary("Standard") ' Librairie qui contient la fenetre de chargement
Fenetre_chargement = CreateUnoDialog(DialogLibraries.Standard.Dialog2)
Fenetre_chargement.setVisible(True) ' affiche la fenêtre de la barre de progression
lien = "0"
j=-1
choix_auto_separateur = 7-Msgbox("Selectionnez les fichiers à importer puis cliquer sur ""Annuler, Voulez-vous que le séparateur de texte soit renseigné automatique?""", MB_YESNO)
rem RECUPERATION DES LIENS DES FICHIER
Do while lien <>""
j=j+1
lien = RechercheFichier
if lien <>"" Then
nb_lignes_totales= nb_lignes_totales +nb_lignes(lien)
endif
collection_lien.Add(lien, Cstr(j))
loop
rem LANCEMENT DU TEMPS DE CHARGEMENT
debut = GetSystemTicks
nb_lignes_faites = 0
rem CONNEXION AVEC LA BASE DE DONNEES
ThisDatabaseDocument.CurrentController.connect("","")
maConnexion = ThisDatabasedocument.CurrentController.ActiveConnection
sRequete = maConnexion.createStatement()
for i=0 to j-1
annee = trouve_annee(collection_lien.Item(Cstr(i)))
Msgbox("Ajout de la table"& table & "des accidents corporel de la circulation de l'année: "&annee)
rem OUVERTURE DU FICHIER CSV EN MODE LECTURE
iNumFichier = FreeFile 'Ouvre un fichier en affectant à celui-ci un numéro non utilisé par un fichier déjà ouvert
sNomFichier = collection_lien.Item(Cstr(i)) 'lien du fichier
Open sNomFichier For Input As #iNumFichier 'Ouvre le contenu du fichier sNomFIchier en mode lecture dans iNumFichier
Line Input #iNumFichier, sLigne ' élimine la ligne d'entête
importer_un_fichier_csv(table, annee, nb_lignes_faites, nb_lignes_totales, iNumFichier, sRequete, debut, choix_auto_separateur, Fenetre_chargement)
Next i
End Sub
Option Explicit
Sub choix_du_separateur(table as string, annee as integer, choix_auto_separateur as Integer) as string
dim separateur_temp as string
if choix_auto_separateur = 1 Then
if annee<20000 and annee>=2019 Then
separateur_temp = ";"
elseif annee = 2009 and table = "caracteristiques" Then
separateur_temp = CHR(9)
elseif annee<2019 and annee>=2005 Then
separateur_temp = ","
else
separateur_temp = Inputbox("Veuillez entrer le séparateur de champs")
endif
else
separateur_temp = Inputbox("Veuillez entrer le séparateur de champs")
endif
choix_du_separateur = separateur_temp
end sub
Option Explicit
Sub importer_un_fichier_csv(table as string, annee as integer, nb_lignes_faites as Long, nb_lignes_totales as Long, iNumFichier as Variant, sRequete as object, debut as long, choix_auto_separateur as integer, Fenetre_chargement as Object) as Long
dim avancement as integer
dim actualisation_temps as integer
dim temps as long
dim separateur as string
dim duree as Integer
dim sLigne as string
dim aTableau as Variant
dim nb_problemes as Integer
duree = Cint((temps-debut)/1000)
nb_problemes = 0
actualisation_temps = 9
separateur = choix_du_separateur(table, annee, choix_auto_separateur)
Do While Not EOF(iNumFichier) 'Boucle tant que l'on arrive pas à la fin du fichier (End Of File)
REM ACTUALISATION DE LA FENETRE DE CHARGEMENT
nb_lignes_faites = nb_lignes_faites + 1
temps = GetSystemTicks
if Cint((temps-debut)/1000) <> duree Then
avancement = nb_lignes_faites/nb_lignes_totales*100
Fenetre_chargement.getControl("ProgressBar1").value= Cint(avancement)
duree = Cint((temps-debut)/1000)
if avancement>=1 then
actualisation_temps =actualisation_temps+1
Fenetre_chargement.getControl("Label1").text = "Temps écoulé: "&Cstr(duree \ 60) &" min "& Cstr(duree MOD 60) &" sec"
if actualisation_temps =10 Then
Fenetre_chargement.getControl("Label2").text = "Temps restant: "& Cstr((duree*100/avancement-duree) \ 60)&" min "& Cstr((duree*100/avancement-duree) MOD 60)&" sec"
actualisation_temps = 0
endif
endif
endif
REM LECTURE DU FICHIER CSV LIGNE PAR LIGNE
Line Input #iNumFichier, sLigne
REM ECLATEMENT DES LIGNES EN CHAMPS - LE SEPARATEUR DE CHAMPS ETANT LE POINT-VIRGULE ";"
aTableau = remplacer_champs_vides_par_un_zero(Split(traitement_csv(sLigne),separateur))
if Ubound(aTableau)>5 Then
execution_requete_SQL(aTableau, table, annee, sRequete)
elseif nb_problemes>15 Then
print("Probleme dans le choix du separateur")
else
nb_problemes=nb_problemes+1
endif
Loop
importer_un_fichier_csv = nb_lignes_faites
End Sub
Sub execution_requete_SQL(aTableau as Variant, table as string, annee as integer, sRequete as object) as string
if table = "caracteristiques" Then
if annee>=2019 and annee<20000 Then
sRequete.executeQuery("INSERT INTO ""caracteristiques"" (""Num_Acc"", ""an"", ""mois"", ""jour"", ""hrmn"", ""lum"", ""agg"", ""int"", ""atm"", ""col"", ""com"", ""adr"", ""lat"", ""long"", ""dep"") VALUES('" & aTableau(0) & "' , '" & aTableau(3) & "' , '" & aTableau(2) & "' , '" & aTableau(1) & "' , '" & aTableau(4) & "' , '" & aTableau(5) & "' , '" & aTableau(8) & "' , '" & aTableau(9) & "' , '" & aTableau(10) & "' , '" & aTableau(11) & "' , '" & aTableau(7) & "' , '" & aTableau(12) & "' , '" & aTableau(13) & "' , '" & aTableau(14) & "' , '" & aTableau(6) &"')" )
elseif annee<2019 and annee>2005 Then
if isNumeric(aTableau(13))and isNumeric(aTableau(14)) Then
sRequete.executeQuery("INSERT INTO ""caracteristiques"" (""Num_Acc"", ""an"", ""mois"", ""jour"", ""hrmn"", ""lum"", ""agg"", ""int"", ""atm"", ""col"", ""com"", ""adr"", ""lat"", ""long"", ""dep"") VALUES('" & aTableau(0) & "' , '" & aTableau(1) & "' , '" & aTableau(2) & "' , '" & aTableau(3) & "' , '" & aTableau(4) & "' , '" & aTableau(5) & "' , '" & aTableau(6) & "' , '" & aTableau(7) & "' , '" & aTableau(8) & "' , '" & aTableau(9) & "' , '" & aTableau(10) & "' , '" & aTableau(11) & "' , '" & replace(Cstr(Clng(aTableau(13))/100000),",",".") & "' , '" & replace(Cstr(Clng(aTableau(14))/100000),",",".") & "' , '" & aTableau(15) &"')")
else
print(isNumeric(aTableau(14)))
endif
else
print( "Veuillez mettre l'année de la table dans le nom du fichier")
endif
elseif table = "usagers" Then
if annee>=2019 and annee<20000 Then
sRequete.executeQuery("INSERT INTO ""usagers"" (""Num_Acc"", ""place"", ""catu"", ""grav"", ""sexe"", ""trajet"", ""secu"", ""locp"", ""actp"", ""etatp"", ""an_nais"", ""num_veh"") VALUES('" & aTableau(0) & "' , '" & aTableau(4) & "' , '" & aTableau(5) & "' , '" & aTableau(6) & "' , '" & aTableau(7) & "' , '" & aTableau(9) & "' , '" & aTableau(10) & "' , '" & aTableau(13) & "' , '" & aTableau(14) & "' , '" & aTableau(15) & "' , '" & aTableau(8) & "' , '" & aTableau(3)& "')")
elseif annee<2019 and annee>2005 Then
sRequete.executeQuery("INSERT INTO ""usagers"" (""Num_Acc"", ""place"", ""catu"", ""grav"", ""sexe"", ""trajet"", ""secu"", ""locp"", ""actp"", ""etatp"", ""an_nais"", ""num_veh"") VALUES('" & aTableau(0) & "' , '" & aTableau(1) & "' , '" & aTableau(2) & "' , '" & aTableau(3) & "' , '" & aTableau(4) & "' , '" & aTableau(5) & "' , '" & aTableau(6) & "' , '" & aTableau(7) & "' , '" & aTableau(8) & "' , '" & aTableau(9) & "' , '" & aTableau(10) & "' , '" & aTableau(11)& "')")
else
print( "Veuillez mettre l'année de la table dans le nom du fichier")
endif
elseif table = "vehicules" Then
if annee>=2019 and annee<20000 Then
sRequete.executeQuery( "INSERT INTO ""vehicules"" (""Num_Acc"", ""senc"", ""catv"", ""occutc"", ""obs"", ""obsm"", ""choc"", ""manv"", ""num_veh"") VALUES('" & aTableau(0) & "' , '" & aTableau(3) & "' , '" & aTableau(4) & "' , '" & aTableau(10) & "' , '" & aTableau(5) & "' , '" & aTableau(6) & "' , '" & aTableau(7) & "' , '" & aTableau(8) & "' , '" & aTableau(2) & "')")
elseif annee<2019 and annee>2005 Then
sRequete.executeQuery( "INSERT INTO ""vehicules"" (""Num_Acc"", ""senc"", ""catv"", ""occutc"", ""obs"", ""obsm"", ""choc"", ""manv"", ""num_veh"") VALUES('"& aTableau(0) & "' , '" & aTableau(1) & "' , '" & aTableau(2) & "' , '" & aTableau(3) & "' , '" & aTableau(4) & "' , '" & aTableau(5) & "' , '" & aTableau(6) & "' , '" & aTableau(7) & "' , '" & aTableau(8) & "')")
else
print( "Veuillez mettre l'année de la table dans le nom du fichier")
endif
elseif table = "lieux" Then
if annee>=2019 and annee<20000 Then
sRequete.executeQuery("INSERT INTO ""lieux"" (""Num_Acc"", ""catr"", ""voie"", ""v1"", ""v2"", ""circ"", ""nbv"", ""pr"", ""pr1"", ""vosp"", ""prof"", ""plan"",""lartpc"", ""larrout"",""surf"", ""infra"", ""situ"", ""env1"", ""vma"") VALUES('" &aTableau(0) & "' , '" & aTableau(1) & "' , '" & aTableau(2) & "' , '" & aTableau(3) & "' , '" & aTableau(4) & "' , '" & aTableau(5) & "' , '" & aTableau(6) & "' , '" & aTableau(9) & "' , '" & aTableau(10) & "' , '" & aTableau(7) & "' , '" & aTableau(8) & "' , '" & aTableau(11)& "' , '" & aTableau(12) &"' , '" & aTableau(13) &"' , '" & aTableau(14) & "' , '" & aTableau(15) & "' , '" & aTableau(16) & "' , '" & "0" &"' , '" & aTableau(17) &"')")
elseif annee<2019 and annee>2005 Then
sRequete.executeQuery("INSERT INTO ""lieux"" (""Num_Acc"", ""catr"", ""voie"", ""v1"", ""v2"", ""circ"", ""nbv"", ""pr"", ""pr1"", ""vosp"", ""prof"", ""plan"",""lartpc"", ""larrout"",""surf"", ""infra"", ""situ"", ""env1"", ""vma"") VALUES('" & aTableau(0) & "' , '" & aTableau(1) & "' , '" & aTableau(2) & "' , '" & aTableau(3) & "' , '" & aTableau(4) & "' , '" & aTableau(5) & "' , '" & aTableau(6) & "' , '" & aTableau(7) & "' , '" & aTableau(8) & "' , '" & aTableau(9) & "' , '" & aTableau(10) & "' , '" & aTableau(11)& "' , '" & aTableau(12) &"' , '" & aTableau(13) &"' , '" & aTableau(14) & "' , '" & aTableau(15) & "' , '" & aTableau(16) & "' , '" & aTableau(17) &"' , '" & "0" &"')")
else
print( "Veuillez mettre l'année de la table dans le nom du fichier")
endif
endif
End Sub
Option Explicit
Sub EffacerTables
dim maConnexion as Object
dim sRequete as Object
dim stSQL as string
dim resuQuery as Object
ThisDatabaseDocument.CurrentController.connect("","")
maConnexion = ThisDatabasedocument.CurrentController.ActiveConnection
sRequete = maConnexion.createStatement()
stSQL = "DELETE FROM ""caracteristiques_baac"""
resuQuery = sRequete.executeQuery(stSQL)
stSQL = "DELETE FROM ""caracteristiques"""
resuQuery = sRequete.executeQuery(stSQL)
stSQL = "DELETE FROM ""usagers"""
resuQuery = sRequete.executeQuery(stSQL)
stSQL = "DELETE FROM ""usagers_baac"""
resuQuery = sRequete.executeQuery(stSQL)
stSQL = "DELETE FROM ""vehicules"""
resuQuery = sRequete.executeQuery(stSQL)
stSQL = "DELETE FROM ""vehicules_baac"""
resuQuery = sRequete.executeQuery(stSQL)
stSQL = "DELETE FROM ""lieux"""
resuQuery = sRequete.executeQuery(stSQL)
stSQL = "DELETE FROM ""lieux_baac"""
resuQuery = sRequete.executeQuery(stSQL)
End Sub
Option Explicit
Function trouve_annee(chaine_de_caract as string) as integer
dim chaine_finale as string
dim premier_chiffre_atteint as integer
premier_chiffre_atteint = 0
dim i as Integer
for i = 1 to LEN(chaine_de_caract)
if IsNumeric(MID(chaine_de_caract, i, 1)) and premier_chiffre_atteint <> -1 and MID(chaine_de_caract, i, 1) <> "-" Then 'Si le caractère est un guillemet
chaine_finale = chaine_finale & MID(chaine_de_caract, i, 1)
premier_chiffre_atteint = 1
Elseif premier_chiffre_atteint = 1 Then
premier_chiffre_atteint = -1
Endif
Next i
trouve_annee = Cint(chaine_finale)
end function
Option Explicit
Function remplacer_champs_vides_par_un_zero(tableau as Variant) as Variant
dim i as integer
for i =0 to Ubound(tableau)
if tableau(i) = "" Then
tableau(i) = "0"
Endif
Next i
remplacer_champs_vides_par_un_zero = tableau
end function
Option Explicit
Function traitement_csv(chaine_de_caract as String) as String
dim entre_guillemets as Integer
dim caract as string
dim i as long
dim taille as long
dim avancement as integer
entre_guillemets = 0 'Permet de savoir si un caractère est entre guillemets
i=1
taille = LEN(chaine_de_caract)
Do While i<=taille
caract = MID(chaine_de_caract, i, 1)
Rem On teste la présence de guillemets, qui délimite les chaines de caractère
if caract = CHR(34) Then
entre_guillemets = 1 - entre_guillemets
Rem On supprime les guillemets et l'indice i contient le caractère suivant
chaine_de_caract = MID(chaine_de_caract,1,i-1)&MID(chaine_de_caract,i+1,taille-i)
i = i-1
taille = taille-1
elseif caract = "'" or caract = ":" Then
Rem Avant les apostrophes, on ajoute un antislash "\"
chaine_de_caract = MID(chaine_de_caract,1,i-1)&MID(chaine_de_caract,i+1,taille-i)
i = i-1
taille = taille-1
elseif entre_guillemets = 1 Then
if caract = ";" or caract = CHR(9) Then
Rem on supprimer les séparateur de champs tab → CHR(9) et ";"
chaine_de_caract = MID(chaine_de_caract,1,i-1)&MID(chaine_de_caract,i+1,taille-i)
i=i-1
taille = taille-1
elseif caract = "," Then
Rem on remplace les "," par des "." pour les nombres décimaux
chaine_de_caract = MID(chaine_de_caract,1,i-1)&"."&MID(chaine_de_caract,i+1,taille-i)
endif
endif
i=i+1
loop
traitement_csv = chaine_de_caract
End function
Option Explicit
Sub nb_lignes(lien as string) as long
Dim oFSO As Variant, Fich As Variant, Ts As Variant
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set Fich = oFSO.OpenTextFile(lien, 1)
Ts = Fich.ReadAll
nb_lignes = Fich.Line
end sub
Option Explicit
Sub RechercheFichier as String
Dim fichier As object
Dim Valeur As Integer
Dim urlCible As String
Rem Ouverture d'une nouvelle fenêtre
fichier = CreateUnoService("com.sun.star.ui.dialogs.FilePicker") 'création du service sélection d'un fichier
Valeur = fichier.Execute()
'si il y au moins un choix
If Valeur = 1 Then urlCible=ConvertFromUrl(fichier.getDirectory()) 'on convertit l'URL
RechercheFichier = urlCible
End Sub
Je vous en remercie par avance.


