begin process at 2012 05 27 19:47:44
  Trouver un code source :
 
dans
 
Accueil > 

Code

 > 

Base de données

 > [CRON] INSERT ON DUPLICATE KEY UPDATE

[CRON] INSERT ON DUPLICATE KEY UPDATE


 Information sur la source

Note :
10 / 10 - par 1 personne
10,00 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10
Catégorie :Base de données Classé sous :REQUETE SQL, DUPLICATE KEY, CRON, INSERT, MULTIPLE Niveau :Initié Date de création :15/10/2011 Date de mise à jour :15/10/2011 11:20:06 Vu :1 506

Auteur : pierreSabatier

Ecrire un message privé
Commentaire sur cette source (4)
Ajouter un commentaire et/ou une note

 Description

Un CRON journalier doit mettre à jour plus de 100000 enregistrements sur une table MYSQL (vous pourriez adapter cela à votre SGBD).

D'un côté, faire un gros delete puis un insert multiple décalait chaque jour les index de la table, laissant des gros trous.
D'un autre, modifier chaque enregistrement prenait beaucoup de temps et de ressources serveur.

D'où cette idée de segmenter une requête grâce à la fonction "VALUES" disponible depuis Mysql 4.1.1 dans un INSERT ON DUPLICATE KEY UPDATE. Par exemple :
INSERT INTO table (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);


Source

  • <?php
  • interface Mode {
  • const PROD = 0;
  • const DEV = 1;
  • const DEBUG_ONE = 2;
  • const DEBUG_ALL = 3;
  • }
  • class SegmenterRequete {
  • private $debut;
  • private $fin;
  • private $requete;
  • private $n;
  • public $separateur = ', ';
  • public $guillemet = "'";
  • public $mode = Mode::PROD;
  • public $pause = 2;
  • public $max = 200;
  • public function __construct($debut, $fin){
  • $this->debut = $debut;
  • $this->fin = $fin;
  • $this->initialiser_requete();
  • }
  • public function __destruct(){
  • // si les enregistrements n'ont pas tous ete faits
  • if (0 < $this->n) {
  • // nettoyer la requete : supprimer son dernier separateur
  • $this->requete = substr($this->requete, 0, -strlen($this->separateur));
  • $this->executer();
  • }
  • }
  • private function initialiser_requete(){
  • // ecrire une nouvelle requete
  • $this->requete = $this->debut;
  • $this->n = 0;
  • }
  • private function executer(){
  • $this->requete .= $this->fin;
  • switch ($this->mode) {
  • case Mode::PROD:
  • mysql_query($this->requete);
  • sleep($this->pause);
  • break;
  • case Mode::DEV:
  • mysql_query($this->requete);
  • // pas de break, on "passe" au cas suivant
  • case Mode::DEBUG_ONE:
  • die($this->requete);
  • break;
  • case Mode::DEBUG_ALL:
  • print($this->requete) . "\n<br/>";
  • break;
  • }
  • }
  • public function ajouter(){
  • return $this->ajouter_tableau(func_get_args());
  • }
  • public function ajouter_tableau($data){
  • $data = array_map('mysql_real_escape_string', $data);
  • $chaine = '(' . $this->guillemet . implode($this->guillemet . ', ' . $this->guillemet, $data) .
  • $this->guillemet . ')';
  • return $this->concatener($chaine);
  • }
  • public function concatener($ch){
  • $this->requete .= $ch;
  • if (++$this->n < $this->max) {
  • $this->requete .= $this->separateur;
  • }
  • else {
  • $this->executer();
  • $this->initialiser_requete();
  • }
  • return $this;
  • }
  • }
  • /** un exemple fictif
  • $Q = new SegmenterRequete('INSERT INTO T (pop, top) VALUES ', ' ON DUPLICATE KEY UPDATE SET pop = VALUES(pop);');
  • $Q->mode = Mode::DEBUG_ALL;
  • $Q->max = 2;
  • $Q->ajouter('lol', 'mom');
  • $Q->ajouter('lol', 'mom');
  • $Q->concatener("('lol','')");
  • */
<?php
interface Mode {
	const PROD = 0;
	const DEV = 1;
	const DEBUG_ONE = 2;
	const DEBUG_ALL = 3;
}
class SegmenterRequete {

	private $debut;
	private $fin;

	private $requete;
	private $n;

	public $separateur = ', ';
	public $guillemet = "'";
	public $mode = Mode::PROD;
	public $pause = 2;
	public $max = 200;


	public function __construct($debut, $fin){
		$this->debut = $debut;
		$this->fin = $fin;

		$this->initialiser_requete();
	}
	public function __destruct(){
		// si les enregistrements n'ont pas tous ete faits
		if (0 < $this->n) {
			// nettoyer la requete : supprimer son dernier separateur
			$this->requete = substr($this->requete, 0, -strlen($this->separateur));
			$this->executer();
		}
	}
	private function initialiser_requete(){
		// ecrire une nouvelle requete
		$this->requete = $this->debut;
		$this->n = 0;
	}
	private function executer(){
		$this->requete .= $this->fin;
		switch ($this->mode) {
		case Mode::PROD:
			mysql_query($this->requete);
			sleep($this->pause);
			break;
		case Mode::DEV:
			mysql_query($this->requete);
			// pas de break, on "passe" au cas suivant
		case Mode::DEBUG_ONE:
			die($this->requete);
			break;
		case Mode::DEBUG_ALL:
			print($this->requete) . "\n<br/>";
			break;
		}
	}

	public function ajouter(){
		return $this->ajouter_tableau(func_get_args());
	}
	public function ajouter_tableau($data){
		$data = array_map('mysql_real_escape_string', $data);
		$chaine = '(' . $this->guillemet . implode($this->guillemet . ', ' . $this->guillemet, $data) . 

$this->guillemet . ')';
		return $this->concatener($chaine);
	}
	public function concatener($ch){
		$this->requete .= $ch;
		if (++$this->n < $this->max) {
			$this->requete .= $this->separateur;
		}
		else {
			$this->executer();
			$this->initialiser_requete();
		}
		return $this;
	}
}
/** un exemple fictif

$Q = new SegmenterRequete('INSERT INTO T (pop, top) VALUES ', ' ON DUPLICATE KEY UPDATE SET pop = VALUES(pop);');
$Q->mode = Mode::DEBUG_ALL;
$Q->max = 2;

$Q->ajouter('lol', 'mom');
$Q->ajouter('lol', 'mom');
$Q->concatener("('lol','')");
 */

 Conclusion

/!\ AVERTISSEMENT : ce code n'est pas testé (celui utilisé en prod est différent et un peu plus archaïque je crois).
Bien qu'étant destiné en priorité à un cron - où les valeurs en entrée sont contrôlées, s'il y a une faille, ce serait sympa de le dire.

Il s'agit d'un code d'initié, plus en fonction de son usage que de sa complexité intrinsèque.


 Historique

15 octobre 2011 11:20:07 :
Je corrige une erreur : array_map('mysql_real_escape_string', $data); devient $data = array_map('mysql_real_escape_string', $data);

 Sources du même auteur

Source avec Zip TESTS UNITAIRES
COMPTEUR DE VISITES SUR FICHIER

 Sources de la même categorie

Source avec Zip ORM : DAO, ACTIVERECORD ET DBLIST par Reldan
Source avec une capture CET EXTRAIT PERMET D'AJOUTER DANS UN TABLEAU UNE AGRÉGATION ... par Denis007
EXPORT DE BASE AU FORMAT CSV par remib74
Source avec Zip RECHERCHE DES DOUBLONS DANS UNE TABLE MYSQL EN SÉLECTIONNANT... par aladec2007
Source avec Zip CLASS MYSQL 5/PHP5 AVEC GESTION DES EXCEPTION ET DES REQUÊTE... par devil_may_cry

 Sources en rapport avec celle ci

Source avec Zip Source avec une capture UPLOADEUR DE FICHIERS MULTIPLES V1 par cod57
Source avec Zip Source avec une capture CONSULTER, INSERER, SUPPRIMER SIMPLEMENT par awaclid
Source avec Zip Source avec une capture UTILISATION DU COMPOSANT MULTIPOWUPLOAD par crazygogo
GÉNÉRATEUR SQL D'INSERTION OU DE MISE À JOUR par 8Tnerolf8
TÉLÉCHARGER PLUSIEURS FICHIERS EN 1 CLIC par Flachy Joe

Commentaires et avis

Commentaire de cod57 le 15/10/2011 10:57:17

bonjour

/*Bonjour je crois qu'il y a problème de sécurité dans une fonction*/
/*http://www.phpsecure.info/v2/article/InjSql.php*/
/*le tableau $data doit etre ecrasé*/
/* mon test */
/*on suppose connecté*/
$link = mysql_connect('localhost','root','');

$Q = new SegmenterRequete('INSERT INTO T (pop, top) VALUES ', ' ON DUPLICATE KEY UPDATE SET pop = VALUES(pop);');
$Q->mode = Mode::DEBUG_ALL;
$Q->max = 2;

$Q->ajouter('lol\'', 'mom');
echo '<br>';
$Q->ajouter('lol', 'mom');
echo '<br>';
$Q->concatener("('lol','')");

/*mon test*/
$data=array("INSERT INTO membres (login,password,nom,email,userlevel) VALUES ('','','','','3')#','1");
print_r($Q->ajouter_tableau($data));
/*fin test*/



//array_map('mysql_real_escape_string', $data);
//mais  $data = array_map('mysql_real_escape_string', $data);
//j'ai mis le return pour le test

//public function ajouter_tableau($data){
//
// $data=array_map('mysql_real_escape_string', $data)
// return $chaine = '(' . $this->guillemet . implode($this->guillemet . ', ' . $this->guillemet, $data) .
//    $this->guillemet . ')';  

?>

Commentaire de pierreSabatier le 15/10/2011 11:25:03

Merci COD57. Effectivement, je m'étais mal renseigné sur la fonction "array_map", j'avais pensé à un passage par référence :p

Maintenant le code est sûr.

Mais attention COD57. Je crois que tu n'as pas bien compris comment utiliser cette classe. La méthode SegmeterRequete::ajouter_tableau prend en paramètre un tableau de valeurs à ajouter, et seulement les valeurs, pas la structure de la requête. Celle-ci est définie par les attributs SegmenterRequete::$debut et SegmenterRequete::$fin.

Commentaire de cod57 le 15/10/2011 15:25:08 10/10

Si j'ai compris ce que tu veux faire avec cette class, mon exemple n'étais que pour mettre en évidence le non filtrage ... bon ... pour ce qui est vraiment utile ton code est intéressant, il faudra que je teste ça sur un gros update. Il y a des astuces très ingénieuses.

++

Commentaire de pierreSabatier le 15/10/2011 15:38:41

Merci COD57 :-) En affectant l'attribut SegmenterRequete::$mode à Mode::DEBUG_ONE, la première requête est affichée. Ca peut être rassurant de voir le résultat avant le gros update ;-)

 Ajouter un commentaire


Discussions en rapport avec ce code source dans le forum

insert multiple de checkbox dans column de table mysql [ par lassad_haddaji ] je cherche des solutions insert multiple de checkbox dans column de table mysql [ par lassad_haddaji ] salut tt le monde, mon probleme est que je veux inserer des champs de checkbox dans une seule solumn de tabe mysql1)fichier formulaire.html&lt;form ac mysql_insert_id [ par algantilla ] bonsoir à tous! en fait j'aimerai bien savoir comment afficher la valeur retournée par mysql_insert_id dans une zone de texte?? encore une petite ques INSERT INTO OU BIEN ..? [ par marouanemarouane ] Bonsoir, j'ai besoin d'un petit aide s'il vous plait, quand j'execute cette requete il ne m'ajoute pas le lien dans mon espace membre, j'ai utilisé WH eviter le cron [ par emiliejolie04 ] Bonjour, J'aimerais trouver une solution pour pouvoir faire une action vu que je ne peux pas utiliser le cron (hebergement mutualisé ne le permettant formualire [ par allia007 ] j'essay de récupérer des information apartir d'un formulaire et l'insséré dans la table.voici le code  j utilisé la requete INSERT mais sa ne marche p mysql_insert_id [ par mickadevelop ] Bonjour,j'ai une question concernant la fonction mysql_insert_id() dans le cadre d'un fonctionnement multi utilisateur n'y as t'il pas de risque de ré Formulaire à enregistrement multiple [ par baldy ] BonjourJe suis actuellement en train de reprendre un code existant. Il s'agit d'un formulaire sensé référencer les revendeurs d'une entreprise. Il fon Insert into avec une seule valeur [ par azqsazqs ] Bonjour,je me pose une question depuis un certain temps, peut-on créer une entrée à partir d'une seule valeur:INSERT INTO clients ('nom_c') VALUES ('$ Solution d'upload multiple NAS Uploader [ par caviar ] Salut à tous, je voulais vous présenter la solution d'upload multiple en flash php que j'ai mise au point ... totalement open source et gratuite :) ça


Nos sponsors


Sondage...

Comparez les prix

CalendriCode

Mai 2012
LMMJVSD
 123456
78910111213
14151617181920
21222324252627
28293031   

Consulter la suite du CalendriCode

A découvrir



 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 1,170 sec (4)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales