Page 1 sur 2

Comparer le count de la requête?

Posté : 12 mai 2008, 21:48
par cicom
Bonjour,
Dans le cadre d'une requête assez complexe j'ai besoin d'utiliser le résultat d'une comparaison directement dans la requête. Cette comparaison me permettra de filtrer énormément de résultats et donc de gagner autant de temps...
Voyez plutôt.

Code : Tout sélectionner

//code de la table puisque c'est obligatoire CREATE TABLE `pub` ( `id` int(11) NOT NULL auto_increment, `camp` int(11) NOT NULL, `pseudo` text NOT NULL, KEY `id` (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=64432 ; //requête en question SELECT count(t1.id) AS compte, t1.camp, t1.pseudo FROM pub t1 JOIN pub t2 ON ( t1.pseudo = t2.pseudo AND t1.camp = t2.camp AND t1.id <> t2.id ) WHERE t1.camp = '227' AND compte > 1 GROUP BY pseudo
Or en faisant ça j'obtiens que la colonne compte n'existe pas etc etc... comment faire?
t1.camp = '227' est amené à disparaitre, c'est juste pour les tests.
Merci de votre aide sinon de votre lecture!

Posté : 12 mai 2008, 22:09
par rami
La clause HAVING pourrait le faire. Essaies ça :
SELECT count(t1.id) AS compte, t1.camp, t1.pseudo
FROM pub t1 JOIN pub t2
ON ( t1.pseudo = t2.pseudo AND t1.camp = t2.camp AND t1.id <> t2.id )
WHERE t1.camp = '227'
GROUP BY pseudo
HAVING (compte > 1)


Posté : 14 mai 2008, 21:31
par cicom
Salut ramis,
J'ai bien testé ta requête et elle me convient bien, si tu pouvais me faire un tout petit topo sur le fonctionnement de HAVING (sinon j'irai chercher si tu ne veux pas) ça serai sympa.
On va déborder sur PHP maintenant, cette requête sert dans un programme de suppression de doubles mais au final le programme me supprime trop de doubles (je crois qu'il ne respecte pas le LIMIT) et les originaux avec. Je voudrais en fait supprimer tous les doublons et laisser un seul enregistrement à chaque fois. Voir mon script.
<?php
include ('gs_includes/scripts.php');
$sql1 = mysql_query("SELECT count(t1.id) AS compte, t1.camp, t1.pseudo FROM pub t1 JOIN pub t2 ON ( t1.pseudo = t2.pseudo AND t1.camp = t2.camp AND t1.id <> t2.id ) WHERE t1.camp = '227' GROUP BY pseudo HAVING (compte > 1)") or die(mysql_error());
while($sql2 = mysql_fetch_array($sql1, MYSQL_ASSOC))
{
 $sql2['compte']--;
 echo $sql2['compte'].' '.$sql2['pseudo'].' <br />';
 if($sql2['compte'] != 0)
 {
  mysql_query("DELETE FROM pub WHERE pseudo = '".$sql2['pseudo']."' AND camp = '".$sql2['camp']."' LIMIT ".$sql2['compte']."");
 }
}
echo 'opération effectuée';
?>
Merci de ton aide!

PS on peut faire autant de bêtises sur la bdd qu'on veut je suis sur le server test donc ça va...

Posté : 14 mai 2008, 21:59
par rami
Pour la clause HAVING, un exempleparmi tant d'autres. google est ton ami ;)

Soit N la valeur renvoyée par le count(). N indique donc le nombre de ligne identiques. Il suffit alors de supprimer (N-1) lignes (si N > 1, sinon on supprimerait toutes les lignes).

Posté : 14 mai 2008, 22:05
par Cyrano
Pour la clause HAVING, je vais te donner un élément de réponse, assez basique pour essayer de rester clair.

En gros, ça revient à dire au serveur :
"retourne moi les valeurs de telle et telle colonne de la table xyz où les conditions .... [ET où la valeur de telle colonne répond à telle condition]

C'est une clause qui ne pourrait pas être intégrée dans une clause WHERE par exemple. On va souvent l'utiliser sur une requête comportant un agrégat du style (très sommairement pour pas compliquer):

Code : Tout sélectionner

SELECT col_1, MAX(col_2) FROM TABLE abcd WHERE col_1 > 10 GROUP BY col_1 HAVING MAX(col_2) > 25
Donc on a une clause de regroupement, mais on ne veut que les lignes où la valeur de l'agrégat (ici MAX(col_2)) est supérieure à 25, ce que tu ne pourrais pas indiquer aussi clairement dans une clause WHERE.

Posté : 15 mai 2008, 18:41
par cicom
Salut,
Ok j'ai compris pour le HAVING merci beaucoup!
Sinon vous ne trouvez rien de spécial dans mon script qui ferait que trop d'enregistrement sont supprimés?
Merci de votre aide!

Posté : 15 mai 2008, 21:06
par rami
Bah je t'ai répondu nan?

Je reprends :
- la requête SELECT te permet de connaître le nombre lignes identiques (appelons ce nombre N)
- dans le DELETE, il suffit donc de supprimer (N-1) lignes.

Tu saisies le principe?

Posté : 16 mai 2008, 19:38
par cicom
Salut,
Oué c'est bien ce que devrait faire mon script en principe mais il me supprime tout en block je ne sais pas pourquoi.
C'est pour cela que je te demande encore un peu d'aide (sait-on jamais un oeil neuf...)
Merci!

Posté : 17 mai 2008, 00:16
par Hywan
Hey :),

Il te suffit de vérifier ce que ton SELECT te retourne. Débug étape par étape.

Posté : 17 mai 2008, 12:26
par cicom
Salut Hywan! Ca fait un bout de temps!
Bon j'ai bien fait tout afficher (même si c'était déjà un peu le cas) et le SELECT a pas l'air bon.
Je prends un exemple, j'ai un enregistrement en 4 exemplaires le SELECT me renvoit qu'il en existe 20...
Que faire?
Merci beaucoup!

Posté : 17 mai 2008, 12:29
par Hywan
Bah fais voir ta requête, car celle donnée par rami est fausse (manque des AS pour le nom des tables).

Posté : 17 mai 2008, 12:54
par cicom
salut,
Les codes de vérification sont aussi dans ce scripts.
<?php
include ('gs_includes/scripts.php');
$sql1 = mysql_query("SELECT count(t1.id) AS compte, t1.camp, t1.pseudo, t1.id FROM pub t1 JOIN pub t2 ON ( t1.pseudo = t2.pseudo AND t1.camp = t2.camp AND t1.id <> t2.id ) WHERE t1.camp = '227' GROUP BY pseudo HAVING (compte > 1)") or die(mysql_error());
while($sql2 = mysql_fetch_array($sql1, MYSQL_ASSOC))
{
 $sql2['compte']--;
 echo $sql2['compte'].' id '.$sql2['id'].' '.$sql2['pseudo'].' <br />';
 if($sql2['compte'] != 0)
 {
  mysql_query("DELETE FROM pub WHERE pseudo = '".$sql2['pseudo']."' AND camp = '".$sql2['camp']."' LIMIT ".$sql2['compte']."");
 }
}
echo 'opération effectuée';
?>
Merci!

Posté : 17 mai 2008, 16:15
par Hywan
Bon voici mes tests, mais je bloque.
On commence par faire notre table de test, et on y insère nos données :

Code : Tout sélectionner

CREATE TABLE IF NOT EXISTS `ma_table` ( `id` int(11) NOT NULL auto_increment COMMENT 'Identifiant.', `champ` varchar(255) NOT NULL COMMENT 'Champ au pif.', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=10 ; INSERT INTO `ma_table` (`id`, `champ`) VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e'), (6, 'f'), (7, 'a'), (8, 'd'), (9, 'a');
Voici mes tests :

Code : Tout sélectionner

mysql> SHOW TABLES; +----------------+ | Tables_in_test | +----------------+ | ma_table | +----------------+ 1 row in set (0.00 sec)
J'ai tout d'abord sélectionner tous les champs avec leur nombre d'apparition. On doit voir apparaître a et d qui apparaissent respectivement 3 et 2. C'est facile, mais c'est pas intéressant. Ce qu'on veut, c'est avoir tous les a et les d listés avec leur id. On aurait alors :

Code : Tout sélectionner

mysql> SELECT a.id, a.champ -> FROM ma_table AS a, -> (SELECT champ, COUNT(champ) -> FROM ma_table -> GROUP BY champ -> HAVING COUNT(champ) > 1) AS b -> WHERE a.champ = b.champ; +----+-------+ | id | champ | +----+-------+ | 1 | a | | 4 | d | | 7 | a | | 8 | d | | 9 | a | +----+-------+ 5 rows in set (0.00 sec)
C'est pas très optimisé. J'ai amélioré la charge mémoire en faisant :

Code : Tout sélectionner

mysql> SELECT DISTINCT * -> FROM ma_table AS a -> WHERE EXISTS (SELECT * -> FROM ma_table AS b -> WHERE a.id <> b.id -> AND a.champ = b.champ); +----+-------+ | id | champ | +----+-------+ | 1 | a | | 4 | d | | 7 | a | | 8 | d | | 9 | a | +----+-------+ 5 rows in set (0.00 sec)
Bien, on a les champs, mais c'est pas encore ce qu'on veut exactement. Quand on a plusieurs fois la même valeur, on veut en conserver une. J'ai pris le choix de conserver la dernière entrée et pas la première. On aurait alors :

Code : Tout sélectionner

mysql> SELECT id, champ -> FROM ma_table AS a -> WHERE a.id < ANY (SELECT id -> FROM ma_table AS b -> WHERE a.id <> b.id -> AND a.champ = b.champ); +----+-------+ | id | champ | +----+-------+ | 1 | a | | 4 | d | | 7 | a | +----+-------+ 3 rows in set (0.00 sec)
Bien, c'est ça.
Maintenant, il faut supprimer. J'ai essayé 10 façons différentes, j'ai toujours la même erreur. J'ai essayé avec ou sans ANY, avec des EXISTS, des IN, des divisions etc., j'ai toujours une erreur avec le DELETE. Alors bon, je vous soumets ce que j'ai fais, si quelqu'un peut aller plus loin, ce sera bien :).

On peut le faire sinon en modifiant la table : soit en modifie les valeurs de champ, soit on crée un nouvel attribut. Mais j'aime pas trop cette méthode, je ne l'ai pas exploré plus que ça.

Sinon, on peut le faire en plusieurs requêtes. On aurait quelque chose du genre :

Code : Tout sélectionner

CREATE TABLE temp_table FROM (SELECT id, champ FROM ma_table GROUP BY champ); DELETE FROM ma_table; INSERT INTO ma_table SELECT * FROM temp_table; COMMIT; DROP TABLE temp_table;
Mais j'ai pas testé ce code. J'ai surtout essayé de le faire en une seule fois …

Wala Monsieur, en espérant que ça avancer un peu les choses.

Posté : 17 mai 2008, 17:02
par cicom
Merci HyWaN d'avoir cherché avec moi pendant un petit bout de temps...
Sinon j'ai trouvé une solution hyper lourde mais qui passe en plusieurs exécutions, ça consiste à tout supprimer et à réenregistrer une seule fois.
Voici le code.
<?php
include ('gs_includes/scripts.php');
$sql1 = mysql_query("SELECT count(t1.id) AS compte, t1.camp, t1.pseudo, t1.id FROM pub AS t1 INNER JOIN pub AS t2 ON ( t1.pseudo = t2.pseudo AND t1.camp = t2.camp AND t1.id <> t2.id ) WHERE t1.camp = '227' GROUP BY pseudo HAVING (compte > 1)") or die(mysql_error());
while($sql2 = mysql_fetch_array($sql1, MYSQL_ASSOC))
{
 $sql2['compte']--;
 echo $sql2['compte'].' id '.$sql2['id'].' '.$sql2['pseudo'].' <br />';
 if($sql2['compte'] != 0)
 {
  mysql_query("DELETE FROM pub WHERE pseudo = '".$sql2['pseudo']."' AND camp = '".$sql2['camp']."'");
  mysql_query("INSERT INTO pub VALUES('','".$sql2['camp']."','".$sql2['pseudo']."')");
 }
}
echo 'opération effectuée';
?>
Mais si certains ont une meilleure solution (plus légère surtout) je suis preneur, sinon je mettrais résolu d'ici à quelques jours...
Merci à tous!

Posté : 17 mai 2008, 18:43
par Cyrano
celle donnée par rami est fausse (manque des AS pour le nom des tables).
Ce n'est pas obligatoire de le mettre, donc ça ne respecte pas de façon stricte le standard, mais c'est valide quand même avec MySQL.
Mais j'ai pas testé ce code. J'ai surtout essayé de le faire en une seule fois …
Et ça n'aurait pas marché : le COMMIT indique que tu tentes une transaction ... sans le début de la transaction, et surtout avec une table MyISAM.