Aide pour optimiser mon code => insert bdd - PHP - Programmation
Marsh Posté le 06-12-2007 à 13:18:32
tu créés une table temporaire ( keyword: temporary) en pensant aux indexs nécessaires.
Tu fais un load data infile dans la dite table de ton fichier csv.
Tu fais tes requêtes de comparaison entre ta table temporaire et ta table destination, puis les insertions.
Marsh Posté le 06-12-2007 à 14:25:57
Oui c'est une solution.
Mais je souhaite etudier l'idée de requetage // à partir des lignes dans mon fichier
Marsh Posté le 06-12-2007 à 14:32:49
bin fais comme tu veux ...
Mais ensuite tu vas te rendre compte que c'est lent, lourd et qu'il va falloir le refaire correctement
Marsh Posté le 06-12-2007 à 14:40:34
Ok,
Le soucy c'est que j'ai 4 tables pour les recherches et update.....
Et je suis pas un AS en sql je dirais.
Marsh Posté le 06-12-2007 à 15:27:31
commence à faire ton truc et reviens poser tes questions sqls quand tu bloques
Marsh Posté le 06-12-2007 à 17:08:59
Voici le shéma de la base :
Code :
|
Mon fichier a traiter ressemble à ca:
Code :
|
A présent je lance un script php avec le nom du fichier a traiter en parametre qui fait ca:
Code :
|
Voila, aujourd"hui ce code fonctionne parfaitement.
Le problème c'est que c'est long car il fait ligne par ligne......Je suis à 10 requetes par secondes car il fait environ 2 lignes par secondes........(et 5 requetes par ligne).......
Sachant que le fichier à insérer contient généralement 20% d'update,10% d'insertion et 70% de doublons, je suis bloqué à ce niveau là.
Marsh Posté le 06-12-2007 à 17:51:09
oui et il est où le problème?
Ce qui est amusant c'est que tu es exactement dans ma 2eme remarque
Marsh Posté le 06-12-2007 à 17:56:30
Le pb est que :
1 => il lit 1 ligne et requete 5 fois pour savoir ce qu'il doit faire (insert/update/nothing).
2 => il attends que la ligne 1 soit passé pour attaquer la ligne 2
Donc c'est chiant sur des milliers de lignes.
Je voudrait bien qu'il lise par exemple 300 lignes, execute en même temps les 300 requetes en même temps, et passe aux suivantes.
Le coup de loader dans une table temporaire, je veux bien, ce sera certainement le plus rapide......
Je sais avoir une table à l'image de mon CSV.
Je ne sais pas comment gérer mes insert/update/notthing apres en sql......
Marsh Posté le 07-12-2007 à 11:44:42
Bon,
J'ai créé une table temporaire, et insérer mon fichier CSV brut dedans.
Le shéma:
CREATE TABLE `tmp2` (
`job_name` varchar(100) NOT NULL,
`job_start` datetime NOT NULL,
`job_end` datetime NOT NULL,
`job_duration` time NOT NULL,
`job_status` varchar(15) NOT NULL,
`job_log` varchar(500) NOT NULL,
`server_name` varchar(45) NOT NULL,
`job_id` bigint(20) NOT NULL auto_increment,
PRIMARY KEY (`job_id`),
UNIQUE KEY `job_name` (`job_id`,`job_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Je suis un peu perdu avec le sql maintenant.
Marsh Posté le 07-12-2007 à 14:32:44
Dans quel cas tu va choisir de faire un update ou rien
Je suis presque sur que tout est faisable en quasiment une seule requête par ligne à coup de IGNORE et de ON DUPLICATE KEY UPDATE. Reste ma question à traiter
Marsh Posté le 07-12-2007 à 15:01:14
ce qui est sur c'est qu'il est dommage que mysql ne supporte pas "merge ... into" car il est sur que ça se faisait en 1 requête
edit: et +1 sur la question précédente
Marsh Posté le 07-12-2007 à 15:09:32
Ok, donc le cas ou je fais un update :
job_name_id/job_start sont pareil mais un des autre champs est différent => update de toutes les colones.
les inserts:
- si le server_name est pas présent dans srv_ref => update avant insertion pour avoir un server_name_id a renseigner dans job_db_copy
- si job_name n'est pas présent dans job_ref => update avant insertion pour avoir job_name_id a renseigner dans job_db_copy
Marsh Posté le 07-12-2007 à 15:35:55
Je suis pas sur d'avoir compris, donc tu peux avoir qu'un couple (job_name_id,job_start) mais dont les valeurs peuvent changer
=>Update systématique car mysql le passera à la trappe si pas besoin
Je comprends pas tes inserts où tu parles d'update alors que j'ai l'impression que tu veux faire une insertion avant?
=> trigger
Marsh Posté le 07-12-2007 à 15:43:47
car dans la table job_db_copy, il y a server_id (ref vers table srv_ref) et job_name_id (ref ver job_ref)
Marsh Posté le 07-12-2007 à 17:08:42
Donc tu inserts dans ces tables pour avoir les valeurs dans la table job_db_copy Donc tu insères avant de faire l'update
Marsh Posté le 07-12-2007 à 17:21:48
PAr l'exemple :
J'ai un ligne
EXPDX84E,2007-01-30 23:50:30,2007-01-30 23:50:30,00:00:00,COMPLETE,/unicenter/log/EXE0PDX8.EXPDX84E.0001.20070130-235030,freo0092
Je teste si le nom de serveur est connu dans srv_ref :
Non => je cré le nom de serveur dans srv_ref et recupere son id pour l'ulitiser dans la table job_db_copy.
Donc oui, avant l'insert.
Je teste si le nom du job est connu dans job_ref :
Non => je cré le nom du job dans job_ref et recupere son id pour l'ulitiser dans la table job_db_copy.
Donc oui, avant l'insert.
Oui => je teste la présence du couple job_name et job_start dans la table job_db_copy
- Non Présent : je peux donc insérer ma ligne dans job_db_copy, car je suis sur que ce n'est pas un doublon ou un doublon a mettre a jour.
- Présent : update dans job_db_copy de ligne a mettre a jour.
Sachant que quand j'insere dans job_db_copy, je dois trouver un server_name_id et un job_name_id a la place des vrais valeurs (histoire que ce soit plus facile a administrer)
Voili, je sais pas si c'est clair....
En gros , pour chaque enregistrement server_name et job_name, ils doivent etre impérativement reférencés dans les tables job_ref et srv_ref (eh oui, comme ca j'ai un id )
Marsh Posté le 07-12-2007 à 17:42:13
Donc utilises les triggers pour tes enregistrements dans les tables de références
Colle un index unique sur ce qui doit l'être, te pose pas de question et fait un insert on duplicate key update: si y'a rien à faire il le fera pas, si y'a duplication de la clé il update ce que tu veux
Fais une requête préparée avec un insert de masse et ça devrait passer bien plus rapidement
Marsh Posté le 07-12-2007 à 17:47:09
Oui ca me semble bien plus simple comme ca.
Dans la pratique, je n'ai pas les connaissances pour le faire. .....
Marsh Posté le 09-12-2007 à 12:41:56
Commence par les inserts, rejouter un index unique c'est rien, au pire utilise phpmyadmin y'a qu'à cliquer.
http://dev.mysql.com/doc/refman/5. [...] index.html
Pour le on duplicate key, suffit de faire ton insert normal et de rajouter "on duplicate key update col=valeur" tout simplement.
Pour le trigger, ça va être le plus dur mais au final rien de bien insurmontable L'avantage c'est que ta logique applicative est déportée sur le sgbd donc plus besoin de s'en occuper et l'intégrité est garantie.
http://dev.mysql.com/doc/refman/5. [...] ggers.html
http://maximilian.developpez.com/m [...] mysql5/#LB
Marsh Posté le 09-12-2007 à 13:39:08
A mon avis tu devrais utiliser des tableaux associatifs de PHP.
Tu fais 4 grosses select sur tes tables de references et tu stock tout dans des tableaux associatifs qui fond correspondre nom => cle.
Ensuite tu boucles sur ton fichier et tu fais
Code :
|
De cette manière tu fais grossir tes tableaux de cle etrangere à la volée sans besoin de faire du SQL pour récupérer les cles.
Marsh Posté le 09-12-2007 à 23:58:58
Ou comment sortir la moissonneuse pour récupérer 2kg de blé...
Surtout que ça change rien au problème de base: vérifier et agir en conséquence
Marsh Posté le 10-12-2007 à 16:43:28
leflos5 => Si ta réflexion s'adresse à ma proposition, je ne vois pas en quoi mon idée est plus couteuse dans la mesure ou il y aura une insert pour chaque ligne de chaque table et les consultations se feront via des tableaux phps donc accès mémoire et non fichiers.
Alors explique moi en quoi c'est contraignant ?
Marsh Posté le 10-12-2007 à 18:13:16
dans un cas tu laisses le sgbd (dont c'est le métier) s'en occuper.
Dans l'autre tu laisses le soin au developpeur de se débrouiller pour gérer correctement des tableaux monstrueux ( enfin qui peuvent l'être s'il y a beaucoup d'enregistrement à faire).
Marsh Posté le 11-12-2007 à 13:59:18
+1
sans parler du coût en mémoire et en traitement
Alors que le sgbd lui mutualisera les ressources utilisées, pas le script
Si c'est une machine dédiée juste pour ça, juste pour quelques fois, je suis d'accord qu'on peut sortir le tractopelle pour un gravier, ça va plus vite que de chercher la balayette qu'on sait plus où elle peut se trouver et faire perdre 2h
Mais faut penser aussi au coût ne serait ce en temps pour les MAJ si doit y en avoir souvent
L'avantage de déplacer la logique applicative sur la base c'est qu'après t'as plus besoin de te soucier de quoi que ça soit à ce niveau et donc plus de bug de donnée.
Marsh Posté le 11-12-2007 à 16:30:30
Vos concepts sont exacts mais s'adapte a des applications pas à des scripts de transitions ou de conversion.
En l'occurence entre générer des SELECT à la volée pour faire des tests de cohérence et la consultation d'un Array, j'opterai pour le Array, d'autant que tu n'as à modifier ta configuration serveur en utilisant ini_set et set_time_limit.
Marsh Posté le 11-12-2007 à 17:53:06
nan mais le truc que t'as pas compris c'est qu'on ne recommande pas non plus de génèrer "DES" selects, mais qu''une seule requête gère l'insertion/mise a jour ( voir de deux si une seule est trop compliquée à écrire).
Et même pour des scripts de transitions ou de conversion, ce que tu recommandes n'est que rarement utilisable.
Il y a quelques semaines, on devait importer le fichier Insee des entreprise dans une base ( 220K lignes pour environ 250Mo) et bien je te garantis qu'en utilisant ta méthode le serveur aurait lentement succombé...
Et pourtant c'était bien un script de transition/conversion.
Marsh Posté le 12-12-2007 à 02:28:03
D'où ma réponse: si c'est pour faire une moulinette foireuse pour une fois ça sera plus rapide à mettre en oeuvre de faire un truc foireux (maintenant un bug au milieu et zou la cohérence d'où l'importance d'avoir une base qui gère le plus possible la cohérence) pour une fois je suis d'accord.
Mais comme le dis anapajari, ça dépend aussi du volume, des performances du système, du temps qu'on a à accorder à la MAJ, de l'impact résonnable que l'on peut s'autoriser...
Bref si c'est pas pour une fois pour quelques milliers de tuples on vire vite au drame en encourageant qui que ça soit à aller dans le sens du script vilain pour "dépanner" qui mettra à genoux les machines avec leurs utilisateurs
Enfin on sait pas vraiment le contexte et les contraintes de temps/disponibilité donc
Marsh Posté le 12-12-2007 à 11:20:02
Il a un fichier de 4Mo et les array qui sont créée à la volés sont la pr gérer l'unicité des clef étrangères donc à priori ne devrait pas grossir des masses :s
Le contexte m'as semblé permettre l'utilisation de cette moulinette qui n'est pas "foireuse".
J'admet qu'elle fait le boulot qui ne lui est théoriquement pas donné à faire mais elle ne le fera pas mal.
Maintenant ce que j'ai pris en compte cette personne à l'air peu familière avec le SQL et j'essaiyai donc de lui expliquer une autre vision du problème.
Pour le SQL sinon l'aspect de faire de l'INSERT IGNORE sur un champ UNIQUE est peut être un procédé un peu compliqué, je te conseillerai dans un premier temps de bien apréhender la différence entre INSERT et REPLACE que mysql propose.
Marsh Posté le 12-12-2007 à 11:23:56
Attention avec REPLACE. Ca revient à faire un delete (basé sur la clé primaire de la table) suivit d'un insert. Si t'as d'autres tables qui utilisent les id de celle ci alors il ne faut surtout pas faire de REPLACE sous peine de ne plus avoir la moindre cohérence entre ta table et les autres.
Marsh Posté le 12-12-2007 à 11:47:10
yellu > Regarde mieux les structures des tables qu'il utilise, moi je vois bien un autoincrément dans job_db_copy, job_ref et srv_ref. Ta remarque est donc fausse. Par contre, vu la structure de sa base, mon intervention est hors sujet vu qu'il ne peut pas faire de replace vu que sa clé primaire contient entre autre la colonne "auto-incrémenté". S'il voulait faire un replace dans ces conditions, alors il faudrait d'abord qu'il cherche les id de la colonne auto-incrémenté et dans ce cas autant faire ensuite un replace classique.
PS : Quelqu'un voit un intérêt à la création d'une clé primaire qui contient à la fois une colonne autoincrément et d'autres colonnes?
Marsh Posté le 12-12-2007 à 13:45:35
Je parle des référent pas de la table principal dans laquelle il n'y aura que des insertions.
Il inserre des objets dans une table cette objet a une clef etrangere, il se demande si il doit créer une ligne pour cette objet etranger ou non, il peut faire un REPLACE dans la table de cet objet étrangère en forçant le primary key avec la valeur qu'il avais dans la table principale.
Marsh Posté le 13-12-2007 à 10:30:45
Bonjour
Je vois qu'il y a matiere.
Le contexte est le suivant :
J'ai un serveur disponible et qui ne sert uniquement à heberger la base Mysql et la partie Apache/PHP.
Je peux etre gourmant donc, pas de haute dispo rien du tout.
Je tente actuellement d'ecrire les requetes qui me permettent de faire les INSERT/UPDATE à partir des données injectées dans la table temporaire mais j'en suis encore......dans le néant
Marsh Posté le 13-12-2007 à 22:42:12
En ce qui me concerne, je maintiens ma proposition pour une seule chose: faire propre et aussi aller là où on maitrise pas pour tester et savoir faire après!
Si ça presse fais le vilain tableau... Ou ta méthode requête par requête pour chaque ligne
Toutes les solutions marcheront, la plus intéressante intellectuellement étant de se servir du sgbd et ses contraintes d'intégrité + trigger (et pourquoi pas une procédure stockée ), ce qui est son boulot, pas besoin de réinventer la roue carrée ( )
Marsh Posté le 06-12-2007 à 12:39:56
Bonjour,
J'ai un probleme de temps de traitement.
Le perimetre :
1 fichier de 4MO typé CSV (',').
Chaque ligne de ce fichier est à insérer dans une base Mysql.
La problématique :
Pour chaque enregistrement (chaque ligne), je dois tester la présence de chaque éléments dans la base avant de l'insérer (5 requetes pa ligne).
Le souhait:
Paralléliser les tests de présence dans la base par lot de ligne.
Genre:
Je lis 100 lignes => execute pour chaque ligne une fonction (en mode simultané, pas 1 par 1).
Environnement:
Je lis mon fichier ligne par ligne avec fgetcsv.
Avez vous des idées ?
Message édité par hornetmen le 06-12-2007 à 17:16:43