Fichier csv -> BDD

Petit nouveau ! | 4 Messages

12 janv. 2017, 19:16

Bonjour,

Je suis face à un mystère.... et pourtant tout paraissait simple au départ. Je veux juste intégrer dans une base de données des champs qui se trouve dans un fichier csv.

Extrait du fichier csv
--------------------------------

Code : Tout sélectionner

01/11/2016 00:00;0;01/11/2016 00:00;0 01/11/2016 00:01;0;01/11/2016 00:01;0 01/11/2016 00:02;0;01/11/2016 00:02;0
Extrait du code php
-----------------------------
function fct_ImportDataCOT_COV($IdFichierImportCOT,$sNomFichier)
{
	$conn=connectMaBase();
	/*Initialisation des variables*/
	$i=0;
	$sFileName="";
	
	/*Ouverture du fichier en lecture seule*/
	$sFileName = './upload/COT/'.$sNomFichier;
	$handle = fopen($sFileName, 'r');
	/*Si on a réussi à ouvrir le fichier*/
	if ($handle)
	{			
		while (($data = fgetcsv($handle, 1000, ";")) !== FALSE) {			
				//on rempli la table  
				$sql = 'INSERT INTO  tvaluecot ';
				$sql = $sql."(DateTxt,Value,IdFichierImportCOT)";
				$sql = $sql." VALUES ("; 
				$sql = $sql."'".$data[0]."',"; 
				$sql = $sql."'".$data[1]."',"; 
				$sql = $sql." 3"; 
				$sql = $sql." )"; 
				//on lance la commande (mysql_query) et au cas où,
				$result1 = $conn->query($sql);
				if (!$result1) 
				{
					printf("ERREUR SQL : %s\n Message d'erreur : %s\n", $sql, $conn->error);
				}	
			}
		}
		/*On ferme le fichier*/
		fclose($handle);
	}
	mysqli_close($conn);
}
// On obtient en base

Code : Tout sélectionner

Jour Value DateTxt DateSimple IdFichierImportCOT 0000-00-00 0 01/11/2016 00:05 3 0000-00-00 0 01/11/2016 00:04 3 0000-00-00 0 01/11/2016 00:03 3 0000-00-00 0 01/11/2016 00:02 3
Mais si je fais cette requete

Code : Tout sélectionner

UPDATE `tvaluecot` SET `DateSimple`= LEFT(`DateTxt`,10)
j'obtiens :

Code : Tout sélectionner

Jour Value DateTxt DateSimple IdFichierImportCOT 0000-00-00 0 01/11/2016 00:05 01/11 3 0000-00-00 0 01/11/2016 00:04 01/11 3 0000-00-00 0 01/11/2016 00:03 01/11 3 0000-00-00 0 01/11/2016 00:02 01/11 3
Alors que j'aurais du avoir

Code : Tout sélectionner

Jour Value DateTxt DateSimple IdFichierImportCOT 0000-00-00 0 01/11/2016 00:05 01/11/2016 3 0000-00-00 0 01/11/2016 00:04 01/11/2016 3 0000-00-00 0 01/11/2016 00:03 01/11/2016 3 0000-00-00 0 01/11/2016 00:02 01/11/2016 3
Au départ je voulais juste initialiser un champ de type date dans "Jour" mais après 4h de recherche je ne vois pas pourquoi il y a un soucis avec les caractères récupérés ? Ca sent un problème de jeu de caractères mais je ne trouve pas où ça bloque.

Merci d'avance pour votre aide,
Olimarine

Avatar de l’utilisateur
Modérateur PHPfrance
Modérateur PHPfrance | 8755 Messages

13 janv. 2017, 13:43

salut,

ton format de date est un format français et il y a peu (pas du tout en fait) de chance que ton sgbd le comprenne nativement.
il faut pour cela lui indiquer le format.
pour cela mysql utilise str_to_date (et date_format pour faire l'inverse).

dans ce cas, si tu as un champs date
$sql .= ' str_to_date(\'' . $data[0] . '\', \'%d/%m/%Y %h:%i:%s\')';

tu peux aussi lire beaucoup plus simplement ton fichier.
avec la fonction glob[url] tu auras un tableau avec une ligne de ... str_getcsv tu auras un tableau correspondant correspondant à la ligne courante.

Si tu utilises PDO je t'invites à utiliser les requêtes préparées plutôt qu'un insert reconstruit a chaque fois (et se sera plus claire à utiliser).

tu peux aussi utiliser un objet SplFileObject pour simplifier encore un peu le code ;) (ou le rendre plus lisible)

je ferais ainsi
<?php
// classe singleton qui gère la connexion à la base de données cela évite d'en créer une a caque fois que l'on a besoin d’accéder à la base ;)
final class MyBdd {
    private static $cnx;
    
    final static function getConnexion(){
        if(self::$cnx === null){
            $options = [
            PDO::MYSQL_ATTR_INIT_COMMAND  => 'SET NAMES utf8',
            PDO::ATTR_EMULATE_PREPARES => false,
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
            ];
            self::$cnx = new PDO('mysql:host=localhost;dbname=test','test','test',$options);
        }
        return self::$cnx;
    }
}

// un objet ce serait mieux :)
function importDataCOTCOV($fileName, $fileId){
    $dbCnx = MyBdd::getConnexion();
    $file = new SplFileObject($fileName);
    $file->setFlags(SplFileObject::READ_CSV);
    // on indique le séparateur csv, ici le point virgule
    $file->setCsvControl(';');
    // préparation de la requête SQL
    $stmt = $dbCnx->prepare('insert into tvaluecot (date1, value, id_fichier) values (str_to_date(:dte,\'%d/%m/%Y %H:%i\'), :value, :fileid)');
    // lui y change jamais à priori
    $stmt->bindValue(':fileid', $fileId, PDO::PARAM_INT);
    while (!$file->eof()) {
        // on récupère la ligne au format csv
        $line = $file->fgetcsv();
        $stmt->bindValue(':dte',$line[0]);
        $stmt->bindValue(':value',$line[1],PDO::PARAM_INT);
        $stmt->execute();
    }
    $stmt->closeCursor();
}

try {
    // le test
    importDataCOTCOV('olimarine.csv',1337);
    // vérification que l'on a bien inséré les données (la table est vide avant le test)
    $cnx = MyBdd::getConnexion();
    $stmt = $cnx->query('select * from tvaluecot');
    $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
    // si tu n'as pas l'extension xdebug  (c'est un tors :) ) utilise var_dump() à la place
    xdebug_var_dump($data);
    
}catch(PDOException $e){
    xdebug_var_dump($e);
}
cela insère une ligne en base par ligne de fichier
avec

Code : Tout sélectionner

01/11/2016 00:00;0;01/11/2016 00:00;0 01/11/2016 00:01;1;01/11/2016 00:01;0 01/11/2016 00:02;2;01/11/2016 00:02;0
j'obtient

Code : Tout sélectionner

array (size=3) 0 => array (size=4) 'id' => int 8 'date1' => string '2016-11-01' (length=10) 'value' => int 0 'id_fichier' => int 1337 1 => array (size=4) 'id' => int 9 'date1' => string '2016-11-01' (length=10) 'value' => int 1 'id_fichier' => int 1337 2 => array (size=4) 'id' => int 10 'date1' => string '2016-11-01' (length=10) 'value' => int 2 'id_fichier' => int 1
la table de test
create table tvaluecot (
id int not null auto_increment primary key,
date1 date not null,
 value int not null,
id_fichier int not null
);

@+
Il en faut peu pour être heureux ......

Petit nouveau ! | 4 Messages

17 janv. 2017, 20:41

Merci beaucoup moogli pour ta réponse et désolé de te répondre si tardivement j'étais happé par le déménagement de mon garage :-(.

Je vais essayer ça et te tiens au courant dès que je trouve du temps;

Petit nouveau ! | 4 Messages

24 janv. 2017, 17:59

Bonjour moogli,

Ca ne fonctionne toujours pas... il ne reconnait pas la date, pourquoi je ne sais pas :(

Voici le message d'erreur :
object(PDOException)#4 (8) { ["message":protected]=> string(82) "SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'Jour' cannot be null" ["string":"Exception":private]=> string(0) "" ["code":protected]=> string(5) "23000" ["file":protected]=> string(74) "D:\Utilitaire\EasyPHP-DevServer-14.1VC9\www\Centroplast\SQLCentroplast.php" ["line":protected]=> int(98) ["trace":"Exception":private]=> array(2) { [0]=> array(6) { ["file"]=> string(74) "D:\Utilitaire\EasyPHP-DevServer-14.1VC9\www\Centroplast\SQLCentroplast.php" ["line"]=> int(98) ["function"]=> string(7) "execute" ["class"]=> string(12) "PDOStatement" ["type"]=> string(2) "->" ["args"]=> array(0) { } } [1]=> array(4) { ["file"]=> string(64) "D:\Utilitaire\EasyPHP-DevServer-14.1VC9\www\Centroplast\Test.php" ["line"]=> int(5) ["function"]=> string(16) "importDataCOTCOV" ["args"]=> array(2) { [0]=> int(1337) [1]=> string(13) "3_11_2016.csv" } } } ["previous":"Exception":private]=> NULL ["errorInfo"]=> array(3) { [0]=> string(5) "23000" [1]=> int(1048) [2]=> string(28) "Column 'Jour' cannot be null" } }
Je joins le fichier csv source mais je ne vois pas pourquoi ça fonctionne pas... argh on ne peut pas ajouter de fichier :-(

Une piste pourrait être la définition de la table :
tvaluecot
Type MyISAM
Interclassement utf8_general_ci

Ou la définition de la base de données
ldde_centroplast
Interclassement = latin1_swedish_ci

Question subsidiaire la focntion glob ne met pas tout en mémoire avant traitement ? Je pose la question car les fichier csv sont plutôt velu dans les 50 000 lignes.

Si quelqu'un a une idée je suis preneur ...

Avatar de l’utilisateur
Administrateur PHPfrance
Administrateur PHPfrance | 7241 Messages

24 janv. 2017, 20:59

Bonjour,

Le message d'erreur indique que tu essaye d'insérer une valeur NULL/vide dans un champ "jour" dont le schéma de la table inerdit spécifiquement qu'il soit NULL...
Column 'Jour' cannot be null
Quand tout le reste a échoué, lisez le mode d'emploi...

Petit nouveau ! | 4 Messages

25 janv. 2017, 13:05

Merci Arthur, pour ta précision. C'était pour dire qu'il y a un soucis avec la récupération du format date car il le comprend comme nul.

J'ai finalement trouvé d'où venait mon problème grace à un camarade qui développe aussi en php.

Le fichier csv était encodé en UCS-2 Little Endian bref si tu le converti en utf8 tout roule. Maintenant il me reste plus qu'à chercher une fonction php qui va me convertir le fichier en utf8 lors du téléchargement pour être sur de ne pas retomber dans le même problème.

Merci à moogli et Atrhur d'avoir passé du temps sur mon problème !

Avatar de l’utilisateur
Modérateur PHPfrance
Modérateur PHPfrance | 8755 Messages

26 janv. 2017, 12:09

salut,

je ne connais pas ce genre de foncition en php pour tout un fichier.
tu peux utiliser mb_convert_encoding pour le faire à chaque ligne (avec parse csv).

@+
Il en faut peu pour être heureux ......