min(champ1 + champ2)

Eléphanteau du PHP | 14 Messages

22 mai 2005, 15:03

je voudrais cherche dans ma table : table , qui comporte 4 champs : champ1, champ2, champ3, champ4

sélectionner le minimum de l'addition de champ2 + champ3
avec comme condition que addition (champ2+ champ3) < champ4

je pense que c'est unpeu comme ça :

SELECT champ1, MIN( champ2 + champ3) FROM table WHERE (champ2 + champ3) < champ4

mais ça marche pas
quelqu'un a une solution ?

j'ai essayé ça sans champ1, et bizarrement ça marche :

SELECT MIN( champ2 + champ3) as mini FROM table WHERE (champ2 + champ3) < champ4

avec champ1 ça marche pas, il faut MIN() soit seul, étonnant

Eléphant du PHP | 287 Messages

22 mai 2005, 17:53

j'ai essayé ça sans champ1, et bizarrement ça marche :

SELECT MIN( champ2 + champ3) as mini FROM table WHERE (champ2 + champ3) < champ4

avec champ1 ça marche pas, il faut MIN() soit seul, étonnant
si tu utilises une fonction du type min() dans un select avec d'autres champs à sélectionner il te faut une clause goup by.
sinon c'est pas la peine

maintenant si tu veux ton 'champ1' dans le résultat de ta requête essaie ça:

Code : Tout sélectionner

SELECT un, (deux + trois) AS somme, deux, trois FROM toto WHERE (deux + trois) < quatre HAVING (deux + trois) = min( deux + trois )
Modifié en dernier par thierry le 22 mai 2005, 23:06, modifié 1 fois.

Eléphanteau du PHP | 14 Messages

22 mai 2005, 23:00

je vais essayer avec HAVING

Eléphanteau du PHP | 14 Messages

23 mai 2005, 11:02

j'ai essayé avec HAVING, ça ne donne rien, il n'y a pas de message d'erreur, mais pas de résultat.

il semble qu'il manque un GROUP BY , mais je ne sais pas comment l'utiliser car tous les enregistrements du champ1 sont distinct

Eléphant du PHP | 287 Messages

23 mai 2005, 12:46

voila comment j'ai testé la requête que je t'ai envoyé:

table toto:
un->int(peu importe)
deux->int(obligatoirement numérique)
trois->int(obligatoirement numérique)
quatre->int(obligatoirement numérique)

étant donnée que la fonction min est dans une clause having, un group by n'est pas nécessaire.

sinon envois le shéma de ta table et ta requête.

Eléphanteau du PHP | 14 Messages

23 mai 2005, 14:07

ma table pays_tbl

champ : -> type :

id_championnat_pays -> int(15)
pays -> varchar(30)
nbr_membre_actif -> int(15)
nbr_membre_attente -> int(15)
nbr_club_jouable -> int(15)


id_championnat_pays et pays sont uniques, ne peut pas y en avoir 2 pareils.

Code : Tout sélectionner

$requete = "SELECT id_championnat_pays , pays, ( nbr_membre_actif + nbr_membre_attente) AS somme, nbr_membre_actif, nbr_membre_attente FROM pays_tbl WHERE ( nbr_membre_actif + nbr_membre_attente) < nbr_club_jouable HAVING ( nbr_membre_actif + nbr_membre_attente) = min( nbr_membre_actif + nbr_membre_attente) ";

Eléphant du PHP | 287 Messages

23 mai 2005, 18:26

en fait je n'ai pas très bien compris pourquoi ça ne marche pas.

j'arrive à provoquer l'erreur que tu me décris mais une subtilité m'échappe:
aucune données ne s'affichent si le minimum de ( nbr_membre_actif + nbr_membre_attente) est inférieur à un lancien minimum de ( nbr_membre_actif + nbr_membre_attente) plus haut dans la table.

en gros si dans ta table il y a pour ( nbr_membre_actif + nbr_membre_attente):
7
9
6
10
12
=>ça marche

si:
7
9
6
10
12
3
=>ça marche pas

maintenant pour y arriver(si tu n'as pas déja trouvé une solution) tu peut faire ça:

Code : Tout sélectionner

SELECT id_championnat_pays , pays, ( nbr_membre_actif + nbr_membre_attente) AS somme, nbr_membre_actif, nbr_membre_attente FROM pays_tbl WHERE ( nbr_membre_actif + nbr_membre_attente) < nbr_club_jouable group by ( nbr_membre_actif + nbr_membre_attente),pays order by ( nbr_membre_actif + nbr_membre_attente)
les sommes les plus basses devraient se trouver en premier.
si tu penses qu'il ne peut pas y avoir d'égalité entre les pays alors enlève 'pays' dans la clause group by et rajoutes lilmit 1 à la fin.

Eléphanteau du PHP | 14 Messages

24 mai 2005, 00:06

avec limit 1 à la fin et en enlevant pays ça marche.

Code : Tout sélectionner

SELECT id_championnat_pays , pays, ( nbr_membre_actif + nbr_membre_attente) AS somme, nbr_membre_actif, nbr_membre_attente FROM pays_tbl WHERE ( nbr_membre_actif + nbr_membre_attente) < nbr_club_jouable group by ( nbr_membre_actif + nbr_membre_attente) order by ( nbr_membre_actif + nbr_membre_attente) limit 1
on peut pas avoir la liste des pays qui ont le même
( nbr_membre_actif + nbr_membre_attente) pour seulement ceux dont ( nbr_membre_actif + nbr_membre_attente) est le plus petit

( nbr_membre_actif + nbr_membre_attente) -> pays
ex 2 espagne
2 allemagne

ici on a qu'un seul pays, celui qui le premier enregistré 2 espagne

Eléphant du PHP | 287 Messages

24 mai 2005, 01:02

en fait tu n'as même pas besoin du group by ou du champs pays (j'ai compliqué pour rien).

maintenant pour gérer les doublons sans sous-requête(à moin que tu ais mysql 4.1) ou en une seule requête, je ne vois plus comment faire.

par contre j'imagine que tu aurasi fais de toute manière une boucle while pour récupérer les résultats de ta requête.
donc là tout ce que tu as à rajouter c'est un if pour tester tous tes champs 'somme' par rapport au premier que tu as reçu. dès qu'il y a une différence tu sors de ton while avec un 'break'.

maintenant si quelqu'un peut m'expliquer le comportement du having sur ce coup là, je suis preneur.

Eléphanteau du PHP | 14 Messages

24 mai 2005, 10:18

comment tu fais le
" if pour tester tous tes champs 'somme' par rapport au premier que tu as reçu. dès qu'il y a une différence tu sors de ton while avec un 'break'."
tu enregistres dans la boucle while chaque 'somme' dans un tableau $tab_somme()

et tu compares ?
$tab_somme() = array();
$i='1';
while($row=mysql_fetch_array($result)){

$tab_somme[$i]=$row['somme'];
if($tab_somme[1] != $tab_somme[$i]){
 break;
}
$i=$i+1;
}

Eléphant du PHP | 287 Messages

24 mai 2005, 11:46

je pensais plus à quelque chose dans ce style:
$row=mysql_fetch_array($result);
$limit=$row['somme'];
//tu traites les données de ta première ligne

//tu crées une boucle pour exploiter les résultats suivants
while($row=mysql_fetch_array($result))
{
    if($row['somme']==$limit)
    {
        //traitement des données
    }
    else
    {
        break;
    }
}

Modérateur PHPfrance
Modérateur PHPfrance | 2575 Messages

24 mai 2005, 12:34

Le Min() est une fonction statistique qui opére dans un groupe.
Si tu calcule le min() dans un ensemble contenant un élément, le résultat logique est cet élément et lui tout seule.
C'est ton cas quand tu fais un SELECT d'un tuple identifié par des indexes uniques.
Le Min() doit être inclus dans un SELECT où aucun champs suplémentaire n'est unique pour favoriser la notion de groupe (0,n enregistrements)
La clause de regroupement (GROUP BY) est recommandée pour les calculs mais pas obligatoire. Car si elle est présente l'algorithme de calcul est beaucoup plus performent et l'ordre de traitement des champs est géré par un classement qui facile la recherche.
Mais quand la clause GROUP BY est utilisée il faut respecter sa synthaxe :
Les champs du GROUP BY doivant être ceux utilisés dans le SELECT (pas forcement dans l'ordre) à l'exception des champs calculés.
Le GROUP BY établie un tri pour faciliter le calcul et peut appliquer un filtrage des données traitées si le HAVING est spécifié.
On comprend pas ça que HAVING va avec (et pas sans) le GROUP BY
HAVING se comporte comme le WHERE sauf que ce dernier est appliqué avant le GROUP BY (il est déconseiller de repeter la même condition de filtrage à la fois dans le WHERE et dans un HAVING si le GROUP BY existe)

Un exemple simple :
-----------------------
Table : Pays (id, nom) où id est une clé primaire et le nom est un index unique
Table : Ville (id, nom, id_pays) où id est une clé primaire et le nom est un index unique et id_pays est une clé étrangère avec doublons

Table : population (id_ville, nb_femmes, nb_hommes) où id_ville est une clé étrangère

Requête qui calcule le nombre Min() de femmes par pays :
SELECT Pays.nom, Min(nb_femmes)
FROM Pays, Ville, population
WHERE Pays.id = id_pays AND Ville.id = id_ville
GROUP BY Pays.nom

Tu vois que le Min() est utilisé dans un SELECT de champs autorisant les doublons et que le GROUP BY spécifie tous les champs non calculés.

Maintenant pour utiliser limiter le résultat de la requête à la France, il faut ajouter un filtre dans la clause WHERE et non un HAVING :

SELECT Pays.nom, Min(nb_femmes)
FROM Pays, Ville, population
WHERE Pays.id = id_pays AND Ville.id = id_ville AND Pays.id="FR"
GROUP BY Pays.nom

Je n'ai pas utilisé le HAVING parceque le WHERE va appliquer le filtrage avant même que le GROUP BY se déclenche : ce qui est plus économique et performent.

Le Having peut intervenir pour filtrer les données calculées qui n'existent pas normalement avant le regroupement.

La suite logique est alors :
Sturcture du résultat (SELECT) + Source (FROM) + Filtrage de base (WHERE) + regroupement d'organisation (GROUP BY) + Filtrage du regroupement et du calcul (HAVING)
Modifié en dernier par sadeq le 25 mai 2005, 09:29, modifié 1 fois.
--------//////----//---//----//////
-------//---//----//---//----//---//
------//////----//////-----//////
-----||--------||--||---||
Prendre le recul n'est pas une perte de temps.


ps: Affrontez moi dans l'arène

Eléphant du PHP | 287 Messages

24 mai 2005, 13:44

en fait tu décris le having tel qu'il est utilisé avec la plupart des bases(du moins postgres et sqlserveur que je connais).
dans ce cas là je suis d'accord avec toi.

maintenant mysql permet l'utilisation du having en utilisant des fonctions d'aggrégation sans avoir à spécifier de group by(ce qui pourrait être utile si les champs de la table sont uniques)
je me suis donc dit(apparament à tort) que les clauses dans le having allaient filtrer le jeux de résultats en se basant sur ce qui était renvoyé par la clause where.

par exemple sur une table simple possédant un seul champs (num) en effectuant cette requête:
select * from toto having num=min(num)
table toto:
3
7
9
=> la requête renvoit 3

table toto
3
7
9
1
=>la requête ne renvoit rien

si maintenant on change la requête:
select * from toto where num>2 having num=min(num)
=>la requête renvoit à nouveau 3

en fait c'est pourquoi est-ce-que la requête ne revoit pas 1 dans le deuxième exemple que je comprend pas.

Modérateur PHPfrance
Modérateur PHPfrance | 2575 Messages

24 mai 2005, 14:29

Pour te confirmer que ce que j'ai dis est bien le standard SQL et la logique ensembliste même. et non seulement le comportement de quelque SGBDR.
Ton exemple est faux parceque tu as choisi des données classées par ordre croissant et le comprtement de MYSQL vis-à-vis de ça le prouve (aucun résultat si les données ne sont pas classées). On peut concidérer que c'est une abération que MYSQL d'accèpter un Having sans GROUP BY parceque justement ce dernier applique un classement avant le HAVING. On peut se poser la question du rôle de WHERE si le HAVING prend sa place! En fait il n'en est rien même sous MYSQL le Having garde sa place derière le GROUP BY : La documentation de MYSQL le confirme d'ailleurs :

N'utilisez pas HAVING pour des éléments qui devraient être dans la clause WHERE . Par exemple, n'écrivez pas ceci :
mysql> SELECT nom_de_colonne FROM nom_de_table HAVING nom_de_colonne > 0;

Ecrivez plutôt cela :
mysql> SELECT nom_de_colonne FROM nom_de_table WHERE nom_de_colonne > 0;

voir : http://www.nexen.net/docs/mysql/annotee ... ien=having
--------//////----//---//----//////
-------//---//----//---//----//---//
------//////----//////-----//////
-----||--------||--||---||
Prendre le recul n'est pas une perte de temps.


ps: Affrontez moi dans l'arène

Eléphant du PHP | 287 Messages

24 mai 2005, 16:43

Pour te confirmer que ce que j'ai dis est bien le standard SQL et la logique ensembliste même. et non seulement le comportement de quelque SGBDR.
je ne remet pas en cause le standart sql(là n'est vraiment pas la question), en revanche je me pose une question sur une fonctionnalitée de mysql.
Ton exemple est faux parceque tu as choisi des données classées par ordre croissant et le comprtement de MYSQL vis-à-vis de ça le prouve (aucun résultat si les données ne sont pas classées).
On peut concidérer que c'est une abération que MYSQL d'accèpter un Having sans GROUP BY parceque justement ce dernier applique un classement avant le HAVING.
mon exemple n'est ni faux, ni vrai mais est juste un exemple qui veut dire: quand je fait ça voila ce qui arrive.
d'autre part un order by permet de classer un jeu de résultat mais ne peut se trouver que après la clause having.
reste donc effectivement le group by, qui est inutile pour ce que je veut faire, c'est à dire ramener la valeur minimum d'une table(si tu me dis que j'avais qu'à faire un order by avec un limit 1, ou autre chose, on va vraiment pas être copain) et non pas toutes les valeurs du champs num.

pour ce qui est de l'aberration de mysql d'autoriser de tels choses là n'est pas non plus le problème, mysql l'autorise et c'est tout.
je veux dire par là que c'est comme si on reprochait à php d'autoriser de ne pas déclarer de variables.
php l'autorise et reste cohérent vu que les scripts fonctionnent, ce qui n'est pas le cas de mysql avec having(j'entend par là qu'il a l'air de l'autoriser seulement si...).
la question du rôle de WHERE si le HAVING prend sa place! En fait il n'en est rien même sous MYSQL le Having garde sa place derière le GROUP BY : La documentation de MYSQL le confirme d'ailleurs :
Citation:

N'utilisez pas HAVING pour des éléments qui devraient être dans la clause WHERE . Par exemple, n'écrivez pas ceci :
mysql> SELECT nom_de_colonne FROM nom_de_table HAVING nom_de_colonne > 0;

Ecrivez plutôt cela :
mysql> SELECT nom_de_colonne FROM nom_de_table WHERE nom_de_colonne > 0;
tu me confirmes que tu n'as pas cerné le problème.
je ne cherche pas à remplacer 'where' par 'having'.
je cherche à filtrer un jeux de résultats par rapport au résultat d'une fonction d'aggrégation sur un champs(donc seulement possible avec un having).

donc oui c'est sympa de ta part de répondre à ma question, mais ne répond pas en m'expliquant que d'ordinaire une voiture posséde 4 roues, ça tout le monde le sais :wink: