Page 1 sur 2

Caractère d'échapement avec LOAD DATA INFILE

Posté : 10 juin 2005, 15:41
par zeus
Bonjour

Je fais un transfert entre une base de données d'une application développée en assembleur et une base de données MyQSL.

Il existe un petit outil qui a été développé qui extrait les données de cette base de données assembleur sous forme de fichier texte de la forme suivante :

Code : Tout sélectionner

"champ1";"champ2";"champ3"; "champ1";"champ2";"champ3"; "champ1";"champ2";"champ3"; "champ1";"champ2";"champ3"; "champ1";"champ2";"champ3";
Ce fichier est de plus de 5000 lignes

Afin de produire des performances optimales, j'ai choisi la fonction SQL LOAD DATA INFILE

Voici un appercu de ma requete :
$str_requete = "LOAD DATA INFILE '".$racine."venus013.txt'
INTO TABLE cabinets 
FIELDS TERMINATED BY ';' ENCLOSED BY '\"'
LINES TERMINATED BY '\n'
IGNORE 1 LINES
(NCODC, HEURES
Or je me suis rendu compte aujourd'hui que certains de mes champs contenaient des \ qui me décalaient mes champs

Outre le fait que cette requete ne génère pas d'erreur dans ce cas, là, c'est assez genant de ce retrouver avec des valeurs fausses.

J'ai donc voulu utilise la clause ESCAPED BY, mais je me retrouve fasse à 2 problèmes :

PROBLEME 1 :

si j'écrit ça :
$str_requete = "LOAD DATA INFILE '".$racine."venus013.txt'
INTO TABLE cabinets 
FIELDS TERMINATED BY ';' ENCLOSED BY '\"' ESCAPED BY '\'
LINES TERMINATED BY '\n'
IGNORE 1 LINES
(NCODC, HEURES
J'ai une erreur qui me dit qu'un ' n'est pas fermé

J'ai donc tenté ça et,

PROBLEME 2 :
$str_requete = "LOAD DATA INFILE '".$racine."venus013.txt'
INTO TABLE cabinets 
FIELDS TERMINATED BY ';' ENCLOSED BY '\"' ESCAPED BY '\\'
LINES TERMINATED BY '\n'
IGNORE 1 LINES
(NCODC, HEURES
Donne le même résultat qu'avant

Je suis en train de tout casser alors avant que je ne tape sur un collègue, est-ce que quelqu'un voit une solution, une erreur ou un conseil ?

Merci infiniment par avance

Posté : 10 juin 2005, 16:21
par zeus
Vous ne comprenez pas le problème ou vous ne savez pas répondre ?

Parce que si vous voulez que je réexplique, il n'y a pas de problème

Posté : 10 juin 2005, 16:27
par Cyrano
Désolé pour ton collègue zeus, j'ai pas la solution ;)

Blague à part: est-ce qu'il n'y aurait pas moyen de modifier la sortie de ta base assembleur en utilisant des caractèrss d'échappement différents pour éviter les conflits ?

Posté : 10 juin 2005, 16:34
par zeus
Non parce que cette application étant en fin de vie, il ne veulent pas perder du temp à la modifier. C'est plutôt à moi de perdre du temp sur une application en début de vie :lol:

Ce que je me suis résolu à faire, c'est de parcourir le fichier en doublant les \ mais j'ai peur pour les temps d'exécution

Surtout que j'ai un fichier de 20 000 lignes à vérifier (pour voir si je n'ai pas le même problème)

Posté : 10 juin 2005, 16:45
par Cyrano
Difficile, je vois pas, quant à faire un essai, il me manque de la matière... :?
à première vue, ta syntaxe est correcte, mais sans faire d'essai je peux pas voir d'erreur non plus

Posté : 10 juin 2005, 16:54
par zeus
Je peux pas te faire parvenir des jeux d'essais car c'est des données confidentielles

Mais laisse tomber, en attendant la réponse, je me suis mis à travailler sur le fichier et je me suis rendu compte que pour parcourir et modifier 5114 lignes, il ne fallait que 1.2 secondes.

Je vais donc travailler dans cette voie.

Merci quand même de ta participation

Posté : 10 juin 2005, 17:06
par Cyrano
ben en fait, c'est pas ton fichier que je voudrais, c'est quelques lignes type et une structure de table correspondant, mais bon, je suis pas meilleur en la matière, j'aurai pas pour autant une réponse satisfaisante. Suffit de trouver l'astuce.

Posté : 10 juin 2005, 17:12
par zeus
Ma table fait 109 champs !!!! Tu es encore sûr de vouloir la description de la table ?

Je te donne un bout de code :

Transfert pur avec LOAD DATA INFILE
$o_DB = mysql_connect("localhost", "root", "");
	mysql_select_db("venus", $o_DB);
	
	$debut = getMicroTime();
	//Transfert depuis le fichier "venus013.txt" vers la table "Cabinets"
	echo "Fichiers 'Venus013.txt' => table 'Cabinets'<br>";
	//On extrait le contenu du fichier + vérification
	if (!$a_file = file($racine."venus013.txt")) {
		//Affichage d'un message d'erreur si problème à l'extraction du fichier
		echo "<font color='#FF0000' size='+2'>Erreur lors de l'ouverture du fichier 'Venus013.txt'<br>L'opération est abandonnée</font>";
		exit;
	} else { //Extraction du fichier réussi
		$message = "";
		foreach ($a_file as $index => $val) {
			//Ces 2 lignes permettent d'enlever le caractère ; de fin de 
			//	chaine puis de découper la chaine selon le caractère ;
			$ligne = preg_replace('#(.*);#', "\\1",$val);
			$a_ligne = preg_split('#;#', $ligne);
			$nb_champ = count($a_ligne);
			//On vérifie que la ligne contient bien 35 champs
			if ($nb_champ <> 109) {
				$message .= "<br>La ligne n°".($index+1)." contient ".$nb_champ." champs";
			}
		}
		if ($message <> "") {
			//Affichage d'un message d'erreur si problème à l'extraction du fichier
			echo "Le fichier 'venus013.txt' n'est pas valide:".$message;
		} else {
			$str_requete = "LOAD DATA INFILE '".$racine."venus013.txt'
INTO TABLE cabinets 
FIELDS TERMINATED BY ';' ENCLOSED BY '\"' ESCAPED BY '\'
LINES TERMINATED BY '\n'
IGNORE 1 LINES
(NCODC, HEURES, ASSISTANCEMATERIELLE, FAX, DISPO1, REINFORMATISATION, DISPO2, DISPO3, BANQUE, ADRESSE_BANQUE, 
NCODC_AUX_CP_BANQUE, CODE_BANQUE, NUMERO_COMPTE, CODE_GUICHET, CLE_BANQUE, PROFESSION_FACTURATION, MAINTENANCE_SUR_SITE, 
TELEPHONE, NOM_FACTURATION, NOM_ETIQUETTE, PRENOM_ETIQUETTE, ADRESSE1_ETIQUETTE, ADRESSE2_ETIQUETTE, LIEN_CP_FACTURATION, 
PROFESSION_ETIQUETTE, LIEN_POLITESSE_ETIQ, ADRESSE1_FACTURATION, ADRESSE2_FACTURATION, LIEN_CP_ETIQUETTE, LIEN_POLITESSE_FACT, 
NB_LICENCES, NB_POSTES_RESEAU, FACONNIER, LIEN_LOG_TELETRANS, LIEN_CC_TELETRANS, LIEN_PROFESSION_DEF, LIEN_VERSIONEQUINOX, 
ABREV_COMMUNE, CODE_POSTAL, COMMUNE, BUREAU_DISTRIBUTEUR, MEDECIN, FINESS_MEDECIN, ABREV_CAISSE, LIBELLE_CAISSE, 
TYPE_DE_SOIN, LIB_SOIN, COEF_SOIN, COMMENTAIRE, POINTEUR_CLES_EQUINOX, LIEN_UTILISATEUR, ANNEE_ASSIST_DEP, INTERDIT,
UTILISE_PLUS, DATE_CREATION_CABINET, TELEPHONE_DOMICILE, LIEN_AGA, LIEN_LOG_REINFO, DATE_COMMANDE, HISTORIQUE, PRIX_LICENCE,
MONTANT_COMMANDE, MONTANT_ACOMPTE, TELETRANS, EXPLIC_COMMANDE, FINANCEMENT, LIEN_PARRAIN, DATE_LIMITE, LIEN_CADEAU_PARRAIN,
EXCLU, MONTANT_REMISE, DATE_DEMO, DATE_CNIL, NUMERO_CNIL, GROUPE, LIEN_JOINT, LIEN_ACTIONSAV, DATE_MATERIEL, LIEN_PROSPECT,
LIBELLE_RIB, PRELEVEMENT, DATE_CADEAU_PAR, DATE_INSTALLATION, TELEPHONE_BANQUE, PROVIDER, LECTEURCARTE, ONDULEUR,
ORDINATEUR, ECRAN, IMPRIMANTE, LIEN_COMMERCIAL, DATE_LIVRAISON, DATE_REVISION, LIEN_TECHNICIEN, FORMAT_LECTEUR,
TYPE_LECTEUR, REPERTOIRE_EQUINOX, ANNEE_SAUVEGARDE, TYPE_MODEM, DATE_DOC_TELETRANS, MICRO_FOURNI, ECRAN_FOURNI,
IMPRIM_FOURNI, MODEM_FOURNI, LECT_FOURNI, ONDUL_FOURNI, GARANTIE, LIEN_FORMATEUR, TLA)";
			echo $str_requete;
			mysql_query($str_requete) or die(mysql_error());
			/*if($o_db->execute($str_requete, 1))
				echo "<font color='#0000FF'>Transfert réussi</font>";
			else
				echo "<font color='#0000FF'>Transfert réussi</font>";*/
		}
	}
	$duree = getMicroTime()-$debut;
	printf("<br>Durée du traitement: %.3f secondes", $duree);
	flush();
[/size]

Posté : 10 juin 2005, 17:50
par Cyrano
Ok, je vois, mais bon, que la table ait 109 ou 12000 champs ne change rien: c'est le principe de fonctionnement. En y réfléchissant, en fait, ça doit pouvoir se simuler avec trois ou quatre champs et un bout de fichier texte. Je vais y penser un petit moment.

Posté : 12 juin 2005, 00:40
par smarties
Si tu fais un stripslashes($rsrc) tu ne devrais plus avoir de probleme avec les antislashes

Posté : 12 juin 2005, 08:25
par Cyrano
smarties, lis bien son truc: il y a des "\" qui font partie du texte qu'on ne peut pas enlever, il faut donc les protéger au lieu de les enlever: donc ce serait à la rigueur un addslashes, mais là, ce n'est pas de PHP qu'on parle mais de SQL... :shock:

Posté : 12 juin 2005, 09:25
par Ripat
Comme tu l'a définie, ton importation posera un problème de décalage pour des lignes comme:

Code : Tout sélectionner

"champ1";"champ2\";"champ3";
où le \ précède le caractère d'encadrement.
qui sera importée comme:

Code : Tout sélectionner

(null) champ2";"champ3 (null)
Par contre une ligne comme:

Code : Tout sélectionner

"champ1";"cha\mp2";"champ3";
Donnera:

Code : Tout sélectionner

champ1 champ2 champ3
Si tu veux conserver les \ dans les champs importés tels quels (sans sa signification de caractère d'échappement), il suffit de déclarer un autre caractère d'échappement. De préférence, un caractère dont tu sais qu'il ne se trouvera pas dans tes données.

Essaye, par exemple:

Code : Tout sélectionner

LOAD DATA INFILE '/chemin/fichier.txt' INTO TABLE cabinets FIELDS TERMINATED BY ';' ENCLOSED BY '"' ESCAPED BY "|" LINES TERMINATED BY '\n'
Ta tentative, plus haut, d'utiliser l'option ESCAPED BY, est sans effet puisque le \ est déjà le caractère d'échappement utilisé par défaut. Il faut le redéfinir.

Posté : 12 juin 2005, 10:23
par zeus
donc tu veux dire que SQL se sert du caractère d'échappement donné par ESCAPED BY pour echapper ?

Si c'est ça, c'est exactement ce que je recherche

Je peux pas tester aujourd'hui vu que je suis pas au taf (hé bah oui, pas le dimanche...) mais je vous tiens au courant demain

Sinon Ripat, le problème que tu soulève est exactement le problème qui m'a poussé à poster ce message :lol: [/b]

Posté : 12 juin 2005, 10:30
par Cyrano
À mon humble avis zeus, si Ripat s'intéresse à ton problème, tu es sauvé d'avance, au niveau SQL et Expressions régulières, il nous bat tous ensembles d'une seule main :langue: (et je n'exagère rien)

Posté : 12 juin 2005, 10:33
par zeus
Tant mieux, ça évitera que je tue un autre collègue...

Je ne sais pas si c'est une déformation du PHP, mais je ne pensais pas qu'on pouvait mettre un autre caractère d'échappement que \

En tout cas merci à vous deux de vous penchez sur mon pb

Il nous prend tout les 2 à une seule main ? un Ganesha et un presque mamouth ? je demande à voir ? :langue: