[RESOLU] Optimisation de script pour controle et insert en sql

Eléphant du PHP | 94 Messages

08 févr. 2016, 14:33

Bonjour les ami(e)s !!! :roll:

Je Cherche actuellement à optimiser une partie de mon script, en effet je reçois un array() via la méthode GET, et ensuite je contrôle si les données sont déjà existante avant insert dans la base !!!

Le problème c'est que ma méthode exécute plusieurs fois la requêtes, et je sais que c'est pas bon, que l'on peut optimiser tout çà en une seule requête mais je ne sais pas comment.

Donc je demande aux experts de me mettre sur la voie afin que j’exécute une seule requête pour l'ensemble de mon array().

Voici le code :
// definition des variables
$id = $_SESSION['sessionutilisateur'];


// test si get existe et n'est pas null
if(isset($_GET['datasend']) && $_GET['datasend'] != NULL) {
	
	// definition de variable array $datasent recu pour exemple
	$datasent = '[
	{"link":"http://google.fr/", "aTime":"123456789"},
	{"link":"http://google.com/", "aTime":"123456789"},
	{"link":"http://google.uk/", "aTime":"123456789"},
	{"link":"http://google.ca/", "aTime":"123456789"},
	{"link":"http://google.us/", "aTime":"123456789"},
	{"link":"http://google.ru/", "aTime":"123456789"}
	]';
	
	$someArray=json_decode($datasend, true);
    foreach ($someArray as $key => $value) {
	$link=$value["link"];
	$time=substr($value["aTime"], 0, 10);
	
	// on verifie si le lien n'existe pas déjà par rapport à l'user
    $sqlverif = "SELECT * FROM user_link WHERE (idlink='".mysqli_escape_string($db,$link)."' AND id='".$id."')";
    $query = @mysqli_query($db,$sqlverif) or die('ERROR SQL !'.mysqli_error($db));
    $count_link = @mysqli_num_rows($query);
	
	// condition de test si pas ds la base (si différent de 1 on insert)
	if($count_link < 1){
		
	// si pas déjà ds la bdd insertion des donnees ds la base
    $sql = 'INSERT INTO user_link VALUES("", "'.mysqli_escape_string($db,$id).'", "'.mysqli_escape_string($db,$link).'", "'.mysqli_escape_string($db,$time).'", vis)';
    @mysqli_query($db,$sql) or die('ERROR SQL !'.mysqli_error($db));
		
	} // fin condition de test si existe ds la base
	
	} // fin boucle foreach
	
} // fin condition get
Merci par avance...
~~~~$$$$www.asptt-dunkerque.com$$$$~~~~

ynx
Mammouth du PHP | 586 Messages

08 févr. 2016, 15:51

Salut,

Voici un début d'optimisation possible (code non testé) :
// tableau d'exemple de données reçus
$datasent = array(
	array('link' => 'http://google.fr/', 'aTime' => '123456789'),
	array('link' => 'http://google.com/', 'aTime' => '123456789'),
	array('link' => 'http://google.uk/', 'aTime' => '123456789'),
	array('link' => 'http://google.ca/', 'aTime' => '123456789'),
	array('link' => 'http://google.us/', 'aTime' => '123456789'),
	array('link' => 'http://google.ru/', 'aTime' => '123456789')
); 

// on regroupe les liens envoyés dans un tableau 
$datasentLinks = array();
foreach ($datasent as $data) {
	$datasentLinks[] = mysqli_escape_string($db, $data['link']); // on évite les injections sql
}

// on récupère tous les liens envoyés qui existent déjà en bdd
$sqlverif = "SELECT idlink FROM user_link WHERE idlink IN ('" . implode("','", $datasentLinks) . "')";
$query = mysqli_query($db,$sqlverif) or die('ERROR SQL !'.mysqli_error($db));
$existedLinks = mysqli_fetch_all($query);

// on initialise la chaine qui va contenir la liste des entrées sql (VALUES)
$insertValues = '';

foreach ($datasent as $data) {
	// si le lien n'est pas dans la bdd
	if (!in_array($data['link'], $existedLinks) {
		// si pas déjà ds la bdd insertion des donnees ds la base
		// on ajoute une virgule en fin de ligne pour ajouter d'autres entrées
		$insertValues = '("", "' . mysqli_escape_string($db,$id) . '", "' . mysqli_escape_string($db,$data['link']).'", "'.mysqli_escape_string($db,$data['aTime']).'", vis),';
	}
}

// si on a au moins un lien à ajouter
if ($insertValues) {
	// on supprime la dernière virgule ajoutée à la fin
	$insertValues = substr($insertValues, 0, strlen($insertValues) - 1);
	
	$sqlInsert = 'INSERT INTO user_link VALUES ' . $insertValues;
	mysqli_query($db,$sql) or die('ERROR SQL !'.mysqli_error($db));
}
L'idée est de récupérer dans la variable $existedLinks l'ensemble des liens qui existent déjà dans la base de données en utilisant l'opérateur IN en sql.
Puis on parcours le tableau des données reçues en vérifiant l'existence en bdd via if(!in_array($data['link'], $existedLinks)
Enfin on concatène toutes les valeurs des données à insérer pour n'avoir qu'une seule requête sous la forme suivante :
INSERT INTO user_link VALUES
("", "id1", "link1", "time1", vis),
("", "id2", "link2", "time2", vis),
("", "id3", "link3", "time3", vis)
Par contre je n'ai pas compris d'où venait la variable $id dans ton code ni pourquoi elle est utilisée dans la requête de vérification.

Bonne journée

Eléphant du PHP | 94 Messages

08 févr. 2016, 18:15

Salu ynx, merci d'avoir répondu !!!

je vais tester ton code, en fait le but est optimiser au niveau vitesse mais sql aussi, j'avais déjà regarder sur ce genre de méthode mais je pigé rien !!!

La variable $id => session id de l'utilisateur
~~~~$$$$www.asptt-dunkerque.com$$$$~~~~

ViPHP
ViPHP | 2577 Messages

08 févr. 2016, 19:09

Bonjour,

Pour faire au plus simple, je crée un index unique sur les colonnes (idlink, id) et je fais chaque insert. En cas d'erreur "duplicate key", j'ignore car c'est qu'il existait déjà.

Eléphant du PHP | 94 Messages

08 févr. 2016, 20:09

Salut Mazari,

Tu as un exemple par rapport a ce que j'ai donné comme code ?

je comprend un peu la logique mais au final on reste sur le m^me nombre de requête ? ou cela réduit le nombre ?
~~~~$$$$www.asptt-dunkerque.com$$$$~~~~

Mammouth du PHP | 2703 Messages

08 févr. 2016, 20:34

avec cette méthode plus besoin de la requete select, une seule requete insert avec la syntaxe :
INSERT INTO user_link VALUES
("", "id1", "link1", "time1", vis),
("", "id2", "link2", "time2", vis),
("", "id3", "link3", "time3", vis)
qui n'ajoutera que ce qui ne fait pas doublon.

Eléphant du PHP | 94 Messages

08 févr. 2016, 21:47

salut or1, tu parle de la solution de Mazarini ?

parce qu'en faite je viens de tester le ignore, mais le probleme c'est que effectivement il bloque avec l'option unique des champs dans sql, or le probleme est qu'il est possible que plusieurs utilisateur ont le m^me lien !!!

ma table est construite de cette façon :

ID|LINK|TIME|USER|VIS

actuelement je boucle une reception array json.

donc comme un gros bourrin, je foreach l'array() json, dans cette boucle je controle 1 par 1 si existe dans base suivant (LINK et USER) et si existe pas on insert ds sql !!!

Donc en gros je demande au spécialiste une méthode non bourrin, afin d'évité le multi-requêtage et soulager mon serveur et ma base !!!

Je sais qu'il existe un moyen de faire une seule requête, un peu du genre comme ynx me l'as démontrer, mais franchement je pige pas trop, de plus il sépare en 2 boucles

Donc le insert ignore ne marchera pas dans mon cas !!! car je peux pas définir un champs unique a part l'id auto-increment.
~~~~$$$$www.asptt-dunkerque.com$$$$~~~~

Mammouth du PHP | 2703 Messages

08 févr. 2016, 22:09

tu crées donc un index unique sur les champs LINK et USER et alors tu peux faire une seule requête.
bon après, il faut voir si cet index ne va pas ralentir le tout dans d'autres cas de figure, qui sont bien plus fréquents et donc plus problématiques pour le serveur.

Eléphant du PHP | 94 Messages

08 févr. 2016, 22:26

je viens de le faire, à moi que je m'y prends mal, j'ai spécifié champ unique pour LINK et USER ds sql, et voici ma requête php :
// si pas déjà ds la bdd insertion des donnees ds la base
 $sql = 'INSERT IGNORE INTO user_link VALUES ("", "'.mysqli_escape_string($db,$userid).'", "'.mysqli_escape_string($db,$link).'", "'.mysqli_escape_string($db,$time).'", vis)';
Ensuite tu parle de prob index ? en faite ensuite le lien est validé avec le champ(vis) qui pass de 0 à 1.
~~~~$$$$www.asptt-dunkerque.com$$$$~~~~

Eléphant du PHP | 94 Messages

09 févr. 2016, 00:20

Je viens de tester le bout de code à ynx, et çà ne fonctionne pas, il insert quand m^me les lignes dupliqué !!!
c'est chiant car je dois controler par raport à 2 where ( idlink et user)

donc ynx cette partie :
$sqlverif = "SELECT idlink FROM user_link WHERE idlink IN ('" . implode("','", $datasentLinks) . "')";
$query = mysqli_query($db,$sqlverif) or die('ERROR SQL !'.mysqli_error($db));
$existedLinks = mysqli_fetch_all($query);
Donc si je comprend bien, la requete parcours toutes les lignes existante ?

Mis franchement j'ai essayé çà ne fonctionne pas, je suis u'on est pas loin, mais c'est compliqué pour ma petite caboche lol :D

j'ai esayé comme ceci sans resultat :
$sqlverif = "SELECT * FROM user_link WHERE idlink IN ('" . implode("','", $datasentLinks) . "')";
$query = mysqli_query($db,$sqlverif) or die('ERROR SQL !'.mysqli_error($db));
$existedLinks = mysqli_fetch_all($query);
et
$sqlverif = "SELECT * FROM user_link WHERE (idlink AND user) IN ('" . implode("','", $datasentLinks) . "')";
$query = mysqli_query($db,$sqlverif) or die('ERROR SQL !'.mysqli_error($db));
$existedLinks = mysqli_fetch_all($query);
et
$sqlverif = "SELECT * FROM user_link WHERE (idlink AND user='".$id."') IN ('" . implode("','", $datasentLinks) . "')";
$query = mysqli_query($db,$sqlverif) or die('ERROR SQL !'.mysqli_error($db));
$existedLinks = mysqli_fetch_all($query);
~~~~$$$$www.asptt-dunkerque.com$$$$~~~~

ynx
Mammouth du PHP | 586 Messages

09 févr. 2016, 15:43

Quelques explications sur l'utilisation de deux boucles dans mon code :

La première boucle sert uniquement à regrouper les liens envoyés par l'utilisateur dans un tableau ($datasentLink), ceci afin de pouvoir réutiliser directement ce tableau avec la fonction implode dans la requête sql (ce qui n'est pas possible avec le tableau $datasent car il s'agit d'un tableau multidimensionnel).
La première requête permet de récupérer les liens dans la base de données qui sont égaux aux liens envoyés par l'utilisateur. Par exemple si le client envoie les liens présents dans le tableau d'exemple $datasent et que la base de données contient les liens http://google.fr/ et http://google.com/, cette requête va retourner un tableau (via fetchAll) contenant ces deux liens.
Une fois que j'ai récupérer les liens existants dans la bdd qui correspondent à des liens envoyés par le client, je parcours alors tous les liens envoyés par le client et j'insère un lien que si celui-ci n'est pas déjà dans la bdd (comme ton premier code). La seule différence ici est qu'au lieu d'exécuter une requête insert pour chaque lien, je regroupe tous les liens à insérer dans une seule requête.

Si tu veux également ajouter une condition sur l'identifiant de l'utilisateur dans la première requête, tu dois pouvoir le faire comme ceci :
$sqlverif = "SELECT idlink FROM user_link WHERE idlink IN ('" . implode("','", $datasentLinks) . "') AND id='" . .mysqli_escape_string($db,$id) . "'";

Eléphant du PHP | 94 Messages

09 févr. 2016, 17:24

Merci ynx pour les explications, c'est plus explicite , je vais tester avec ce que tu viens de me donner !!! je te tiens au courant merci.
~~~~$$$$www.asptt-dunkerque.com$$$$~~~~

Eléphant du PHP | 94 Messages

09 févr. 2016, 22:07

Super Ynx !!! =D>

Enfin, j'ai résolu le problème, un très grand merci à toi pour ton aide, en fait il fallait bien dans la requete (select) je selection avec la clause where (idlink + user), mais aussi dans le in_array(), inclure l'user pour la comparaison tableau !!!

et donc çà donne ceci :
foreach ($datasent as $data) {
  // si le lien n'est pas dans la bdd
  if (!in_array(array($user, $data['link']), $existedLinks) { // <= ic car oui il scanner que les liens, par les liens where (user+idlink) de $existedLinks([0]=>idlink, user, etc ...)
    // si pas déjà ds la bdd insertion des donnees ds la base
    // on ajoute une virgule en fin de ligne pour ajouter d'autres entrées
    $insertValues = '("", "' . mysqli_escape_string($db,$id) . '", "' . mysqli_escape_string($db,$data['link']).'", "'.mysqli_escape_string($db,$data['aTime']).'", vis),';
  }
}
Je marque donc en résolu !!!

j'ai fait un test de rapport d'optimisation
//avant
exemple 12 requetes en 0.06s
//maintenant grace à ynx 2 requetes (select+insert) en 0.003sec
~~~~$$$$www.asptt-dunkerque.com$$$$~~~~