min(champ1 + champ2)

Répondre


Cette question est un moyen d’empêcher des soumissions automatisées de formulaires par des robots.
Smileys
:D :) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :wink: :!: :?: :idea: :arrow: :| :mrgreen: =D> #-o =P~ :^o :non: :priere: 8-|
Voir plus de smileys
  Revue du sujet
 

  Étendre la vue Revue du sujet : min(champ1 + champ2)

par thierry » 25 mai 2005, 12:44

C'est toi thierry qui a demandé à quelcun d'expliquer le comportement de HAVING et je l'ai fait.
c'est sûr que vu comme ça :)
j'aurais dû préciser le "sur ce coup là".
Comme tu l'as bien expliqué t'as raison d'utiliser le having mais désolé pas sans GROUP BY. Mais pour ajouter le group by il ne faut pas avoir un champs unique dans SELECT (sinon chaque num retourne un min())
c'est vrai qu'apparament mysql n'aime pas trop qu'on utilise having sans group by.
ceci dit:
reste donc effectivement le group by, qui est inutile pour ce que je veut faire... et non pas toutes les valeurs du champs num.
maintenant j'imagine que si mysql autorise le having sans group by c'est bien pour en faire quelquechose.
et c'est précisément ce quelquechose que je ne vois pas.

par sadeq » 25 mai 2005, 09:53

C'est toi thierry qui a demandé à quelcun d'expliquer le comportement de HAVING et je l'ai fait.
maintenant si quelqu'un peut m'expliquer le comportement du having sur ce coup là, je suis preneur.
La voiture à 4 roues dans SQL est si tu utilise Having il faut utiliser GROUP BY (sinon pas de GROUP BY) Et moi j'ai jamais parlé de Order By.

Maintenant pour ta requête :
select * from toto having num=min(num)
Comme tu l'as bien expliqué t'as raison d'utiliser le having mais désolé pas sans GROUP BY. Mais pour ajouter le group by il ne faut pas avoir un champs unique dans SELECT (sinon chaque num retourne un min())

par thierry » 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:

par sadeq » 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

par thierry » 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.

par sadeq » 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)

par thierry » 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;
    }
}

par ginkobiloba » 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;
}

par thierry » 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.

par ginkobiloba » 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

par thierry » 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.

par ginkobiloba » 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) ";

par thierry » 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.

par ginkobiloba » 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

par ginkobiloba » 22 mai 2005, 23:00

je vais essayer avec HAVING