[Résolu][Base] optimisation du traitement de gros fichiers CSV

Discussions et questions sur tout ce qui concerne la programmation tous langages et tous modules confondus.

Modérateur : Vilains modOOs

Règles du forum
:alerte: Balisage obligatoire dans cette section !
Aidez-nous à vous aider au mieux en balisant correctement votre question : reportez-vous sur les règles de cette section avant de poster !
gobsan
NOOuvel adepte
NOOuvel adepte
Messages : 16
Inscription : 24 juil. 2024 06:41

[Résolu][Base] optimisation du traitement de gros fichiers CSV

Message par gobsan »

Bonjour,

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
Vous trouverez également en pièce jointe mon fichier libreoffice base.

Je vous en remercie par avance.
Vous ne pouvez pas consulter les pièces jointes insérées à ce message.
Dernière modification par gobsan le 24 juil. 2024 14:26, modifié 4 fois.
LibreOffice 7.1.7 sous Windows 10 x64
Avatar de l’utilisateur
Bidouille
RespOOnsable forum
RespOOnsable forum
Messages : 12895
Inscription : 08 nov. 2005 16:23
Localisation : Brest, France

Re: optimisation du traitement de gros fichiers csv

Message par Bidouille »

Bonjour,

Cette section a des règles très précises que vous devez obligatoirement suivre pour obtenir de l'aide. C'est indiqué dans le cadre rouge en haut de la page.

Il est demandé aux auteurs de faire précéder le titre de leur question d'une balise adéquate. Ce balisage est extrêmement important car il permet d'avoir une base de connaissance optimum en cas de recherche. Avez-vous remarqué comment étaient les autres questions postées ?

Lisez ce fil pour savoir quelle balise utiliser : ftopic1369.html


Un titre accrocheur vous permettra d'une part d'être aidé plus rapidement et d'autre part, d'enrichir notre base de connaissances.

Nous l'avons fait pour vous cette fois-ci
mais lors de votre prochaine question, nous vous remercions de le faire vous-même sous peine de voir votre sujet verrouillé.

Merci de votre collaboration.
gobsan
NOOuvel adepte
NOOuvel adepte
Messages : 16
Inscription : 24 juil. 2024 06:41

Re: [Base] optimisation du traitement de gros fichiers CSV

Message par gobsan »

Je tiens à m'excuser, je ferai plus attention la prochaine fois, aussi je viens de corriger une petite erreur dans mon code. Je renvoi donc mon code suite à cette correction:

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
	temps = GetSystemTicks
	duree = Cint((debut-temps)/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
Veuillez également retrouver mon fichier contenant les macros dans le module macro_extraction.
Vous ne pouvez pas consulter les pièces jointes insérées à ce message.
LibreOffice 7.1.7 sous Windows 10 x64
Avatar de l’utilisateur
Dude
IdOOle de la suite
IdOOle de la suite
Messages : 26242
Inscription : 03 mars 2006 07:45
Localisation : 127.0.0.1

Re: [Base] optimisation du traitement de gros fichiers CSV

Message par Dude »

Salut,
gobsan a écrit : ↑24 juil. 2024 07:03 Je sais qu'il existe une extension pour cela
Et as-tu testé l'extension en question ?
ImportCSV passe par le service c.s.s.sdbc.DriverManager pour lire le CSV et l'écrire dans une table.
C'est le processus d'écriture qui prend le plus de temps avec environ 2.000 à 2.500 lignes par minute suivant la puissance de ta machine.
Enfin, je ne suis pas sûr qu'au final, intégrer autant de CSV dans un ODB soit une bonne chose.
La taille du fichier chargée en mémoire sera sûrement source de plantage.
gobsan
NOOuvel adepte
NOOuvel adepte
Messages : 16
Inscription : 24 juil. 2024 06:41

Re: [Base] optimisation du traitement de gros fichiers CSV

Message par gobsan »

Bonjour, merci pour ta réponse rapide,

J'ai essayé en effet, mais il fallait rentrer à chaque fois le nom des champs, leurs types et je ne pense pas que cette extension permet d'ajouter les lignes à des tables.

Je n'avais jamais utilisé libreoffice base, je pensais que se serait le mieux car il faut que j'établisse des relations entre les tables. Il y a t'il une meilleure alternative?

Sinon, au niveau des performances, ma macro fait 60 000 lignes en 7 min environ mais j'avais réussi à descendre à 2 min pour 60 000 lignes avec un code moins structuré (j'ai créer une fonction plus générique, mais plus lente). Cependant c'est loin d'être assez pour importer rapidement plusieurs millions de lignes. Aussi, comme tu l'as mentionné, bien qu'au bout d'une heure, j'arrive à télécharger une table complète (sur 4), j'ai l'impression que la table est chargé en mémoire vive, ce qui fait, ralentir voir planter libreoffice base.

Est-ce que les tables peuvent être chargés en mémoire morte?
Sinon, comment travailler avec des relation entre les tables?
LibreOffice 7.1.7 sous Windows 10 x64
gobsan
NOOuvel adepte
NOOuvel adepte
Messages : 16
Inscription : 24 juil. 2024 06:41

Re: [Base] optimisation du traitement de gros fichiers CSV

Message par gobsan »

Pour ne pas avoir une base de données trop importante, je vais alors filtrer mes données pour importer les données suivant un filtre. Je vais également réutiliser mon ancienne macro, qui permet d'importer 60 000 lignes en un peu plus de 2min. Cependant, j'avais déjà réaliser un filtre auparavant et le temps d’exécution était relativement le même. Avez-vous un conseil pour filtrer efficacement un csv suivant un champ?

La modération vous a écrit: :alerte: Merci de ne pas poster plusieurs messages à la suite !
Tant que vous n'avez pas reçu de nouvelle réponse, cela ne sert à rien et votre monologue rallonge inutilement la lecture du fil de discussion.
Si vous devez ajouter un complément d'information, le bouton "Modifié" (petit crayon) à la droite du message permet d'y remédier.

En attendant une prochaine réponse, vous pouvez participer également en répondant à d'autres questions sur notre forum.

LibreOffice 7.1.7 sous Windows 10 x64
Avatar de l’utilisateur
Dude
IdOOle de la suite
IdOOle de la suite
Messages : 26242
Inscription : 03 mars 2006 07:45
Localisation : 127.0.0.1

Re: [Base] optimisation du traitement de gros fichiers CSV

Message par Dude »

gobsan a écrit : ↑24 juil. 2024 09:02 j'ai l'impression que la table est chargé en mémoire vive, ce qui fait, ralentir voir planter libreoffice base.
Qu'ais-je écrit plus haut ?
Dude a écrit : ↑24 juil. 2024 08:18 La taille du fichier chargée en mémoire sera sûrement source de plantage.
Lire : prévention de la corruption des fichiers .odb
gobsan
NOOuvel adepte
NOOuvel adepte
Messages : 16
Inscription : 24 juil. 2024 06:41

Re: [Base] optimisation du traitement de gros fichiers CSV

Message par gobsan »

Est-ce que pour filtrer efficacement, il existe un moyen de trouver la première ligne où une occurrence (par exemple le code INSEE) est présent, de lire la ligne puis de passer à la ligne suivante qui contient cette occurrence?

Pour le moment, ma boucle for, parcourt toutes les lignes et j'utilise instr() pour chaque ligne, je met 1min39 pour 60 000 lignes mais j'aimerai faire mieux.
LibreOffice 7.1.7 sous Windows 10 x64
Avatar de l’utilisateur
Dude
IdOOle de la suite
IdOOle de la suite
Messages : 26242
Inscription : 03 mars 2006 07:45
Localisation : 127.0.0.1

Re: [Base] optimisation du traitement de gros fichiers CSV

Message par Dude »

Tout traitement supplémentaire ralentira forcément l'importation.
Tu fais un Split de ta ligne et tu regardes si les éléments correspondent à ton filtre.
gobsan
NOOuvel adepte
NOOuvel adepte
Messages : 16
Inscription : 24 juil. 2024 06:41

Re: [Base] optimisation du traitement de gros fichiers CSV

Message par gobsan »

Je ne fais pas le split() avant car il y a un traitement important du texte avant le split(), j'obtiens les infos d'une année en moins de 5 minutes, donc je pense que ça ira.

Je tiens à vous remercier pour votre aide. Je pensais que le temps d’exécution était trop lent mais en testant chaque caractère, c'est peut-être pas un temps si mauvais.

Je mets le sujet en résolu.
LibreOffice 7.1.7 sous Windows 10 x64
Avatar de l’utilisateur
micmac
RespOOnsable forum
RespOOnsable forum
Messages : 10152
Inscription : 28 août 2010 08:45

Re: [Base] optimisation du traitement de gros fichiers CSV

Message par micmac »

gobsan a écrit : ↑24 juil. 2024 13:49 Je mets le sujet en résolu.
Pas fait.

Comment clore un sujet lorsqu'il est résolu
Touche Ctrl de Windows = touche cmd⌘ sur Mac
OpenOffice > Outils > Options sur Windows = OpenOffice > Préférences sur macOS