Comparer le count de la requête?

Mammouth du PHP | 555 Messages

12 mai 2008, 21:48

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!
A+ et bon surf!!!!

Mammouth du PHP | 983 Messages

12 mai 2008, 22:09

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)


Mammouth du PHP | 555 Messages

14 mai 2008, 21:31

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...
A+ et bon surf!!!!

Mammouth du PHP | 983 Messages

14 mai 2008, 21:59

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).

Mammouth du PHP | 19672 Messages

14 mai 2008, 22:05

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.
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Mammouth du PHP | 555 Messages

15 mai 2008, 18:41

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!
A+ et bon surf!!!!

Mammouth du PHP | 983 Messages

15 mai 2008, 21:06

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?

Mammouth du PHP | 555 Messages

16 mai 2008, 19:38

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!
A+ et bon surf!!!!

ViPHP
ViPHP | 4674 Messages

17 mai 2008, 00:16

Hey :),

Il te suffit de vérifier ce que ton SELECT te retourne. Débug étape par étape.
« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

Hoa : http://hoa-project.net (sur @hoaproject).

Mammouth du PHP | 555 Messages

17 mai 2008, 12:26

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!
A+ et bon surf!!!!

ViPHP
ViPHP | 4674 Messages

17 mai 2008, 12:29

Bah fais voir ta requête, car celle donnée par rami est fausse (manque des AS pour le nom des tables).
« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

Hoa : http://hoa-project.net (sur @hoaproject).

Mammouth du PHP | 555 Messages

17 mai 2008, 12:54

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!
A+ et bon surf!!!!

ViPHP
ViPHP | 4674 Messages

17 mai 2008, 16:15

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.
« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

Hoa : http://hoa-project.net (sur @hoaproject).

Mammouth du PHP | 555 Messages

17 mai 2008, 17:02

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!
A+ et bon surf!!!!

Mammouth du PHP | 19672 Messages

17 mai 2008, 18:43

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.
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe: