Page 1 sur 1

Je n'arrive pas à faire mon select comme il faut...

Posté : 09 févr. 2006, 10:52
par charles59
Bonjour,

Voici une p'tite capture de ce que j'aimerai faire :

Image



Voilà, donc le premier tableau est ma table
Et le second tableau serait mon résultat que je souhaiterai obtenir...

Ma premiere question : croiyez-vous que s'est faisable ?

Merci d'avance ;)
Charles[/img]

Posté : 09 févr. 2006, 11:17
par Hubert Roksor
Ce que tu veux faire c'est compter le nombre de notes, groupées par élève, par date et par note.

Une simple requête te donnera toutes ces infos:

Code : Tout sélectionner

SELECT Eleve, Date, Note, COUNT(*) AS total FROM table GROUP BY Eleve, Date, Note
Maintenant, çà n'affichera pas les données dans un tableau comme tu le souhaites. D'ailleurs, ça ne renverra pas les dates où il n'y a eu aucune note, ni les notes qui n'ont pas été attribuées. La fonction d'une base de données est de fournir une réponse à une question, pous la mise en forme c'est au niveau de l'application qu'il faudra l'implémenter.

Posté : 09 févr. 2006, 11:33
par Maitrepylos
Bonjour,

essaye ceci

Code : Tout sélectionner

SELECT Eleve, SUM(CASE WHEN Note = 'A' ELSE 0 END) AS A, SUM(CASE WHEN Note = 'B' ELSE 0 END) AS B, SUM(CASE WHEN Note = 'C' ELSE 0 END) AS C, SUM(CASE WHEN Note = 'D' ELSE 0 END) AS D, Date AS Date From tatable
MaitrePylos

Posté : 09 févr. 2006, 11:53
par charles59
Je vais tester tout ça :)
Je ne connaissais pas cettre syntaxe !

Merci à vous 8)

Posté : 09 févr. 2006, 15:03
par charles59
Maitrepylos, tu as sûr qu'il est possible d'utiliser cette syntaxe en Mysql ?

Posté : 09 févr. 2006, 15:13
par Hubert Roksor
Ce qu'il voulait écrire était sûrement ça:

Code : Tout sélectionner

SELECT Eleve, SUM(CASE Note WHEN 'A' THEN 1 ELSE 0 END) AS A, SUM(CASE Note WHEN 'B' THEN 1 ELSE 0 END) AS B, SUM(CASE Note WHEN 'C' THEN 1 ELSE 0 END) AS C, SUM(CASE Note WHEN 'D' THEN 1 ELSE 0 END) AS D, Date FROM table GROUP BY Date, Eleve ORDER BY Date DESC, Eleve ASC
...mais je pense que c'est une erreur de le faire de cette façon, à part s'il s'agit d'une requête que tu utilises dans phpMyAdmin de temps à autres pour te faire une idée de ce qui se trouve dans la table. S'il s'agit d'une application, je te recommanderais plutôt la méthode que j'ai décrite plus haut.

Posté : 09 févr. 2006, 15:31
par Invité
C'est une requête croisée qui calcule le nombre de mêmes notes attribuées aux élèves par date.

J'ai une proposition qui répond à ce que tu veux faire, mais il faut suivre le raisonnement pour comprendre :

1. Construisons la requête qui affiche la colonne A telque tu veux : c'est à dire où on voit pour une date et un élève le nombre total des notes A.
Par exemple : le 08/02/2005 l'élève 1 à eu 2 notes A.
Voici donc la requête :
SELECT count(note) as A
FROM notes as n
WHERE n.note = "A" 
GROUP BY n.date, n.eleve
Regroupement par date et eleve, sélection de la note A ciblée et comptage (count)
Cette requête ne traite donc que les élèves qui ont eu une note A

On peut donc répéter la même requête pour les notes B, C et D pour obtenir leur colonne

Maintenant tout en restant sous SQL, on doit essayer de mixer les différentes colonnes obtenues par les requêtes expliquées précédement.

Commençons par créer l'ossature de la requête finale qui doit construire le tableau croisé qui doit contenir :
une colonne pour les élèves
Les 4 colonnes A, B, C et D pour les comptes des notes (obtenues par les requêtes précédentes)
et finalement la colonne pour les dates

La source de données pour cette requête est notre table notes.
Voici d'abord la requête principale qui extrait les éléves et les dates à partir de la source notes en la triant par date décroissante (exactement comme tu veux)
SELECT eleve,  date
FROM notes
GROUP BY date, eleve
ORDER BY date desc
Il faut grouper par date et eleve pour eliminer les couples (date, eleve) en double (puisqu'un même eleve se voit attribuer plusieurs notes dans une même date)
En effet la requête principale effectue ce regroupement pour créer l'ossature du tableau croisé où un élève ne se répéte que si la date change.

Et enfin ajoutons les colonnes A, B, C et D à cette requête pour obtenir le tableau finale.

Sans oublier qu'on a déjà créé les requêtes qui produisent ces colonnes, il il est question maintenant de savoir comment les connecter à la requête principale.

C'est simple, pour connecter par exemple la requête de la colonne A il faut :
1. d'abord l'insérer dans le SELECT de la requête principale (avec les champs "eleve" et "date") en lui attribuant un alias (nom)

Exemple : SELECT eleve, date, (SELECT ..... ) as A ....

2. connecter la colonne insérée en lui passant comme paramètres les liens nécessaires pour conditionner son traitement.

Dans notre cas toutes les sous-requêtes des colonnes A, B, C et D doivent pointer sur le même élève et date pour synchroniser leur traitement.

Quand une sous-requête de colonne n'arrive pas à compter des notes (puisqu'elle ne fait que ça) dans les conditions précisées par les paramètres, elle renvoit NULL.

Exemple de paramètrage pour connecter la colonne A à la requête principale :
SELECT eleve, date,

(SELECT count(note)
FROM notes
WHERE note = "A"
AND eleve = PRINCIPAL.eleve AND date=PRINCIPAL.date
GROUP BY date, eleve, note) as A

FROM notes as PRINCIPAL
GROUP BY date, eleve
ORDER BY date desc

Dans cet exemple on a ajouté dans la clause WHERE de la colonne A la condition de liaison avec eleve et date da la requête principale.

Ce qui donne par extension, la requête finale qui contient :
L'élève, les 4 colonnes A, B, C et D et la date
SELECT eleve, 

(SELECT count(note)
FROM notes
WHERE  note = "A" and  eleve = PRINCIPAL.eleve and  date=PRINCIPAL.date
GROUP BY  date,  eleve,  note) as A , 

(SELECT count(note)
FROM notes  
WHERE  note = "B" and  eleve = PRINCIPAL.eleve and  date=PRINCIPAL.date
GROUP BY  date,  eleve,  note) as B , 

(SELECT count(note)
FROM notes  
WHERE  note = "C" and  eleve = PRINCIPAL.eleve and  date=PRINCIPAL.date
GROUP BY  date,  eleve,  note) as C , 

(SELECT count(note)
FROM notes  
WHERE  note = "D" and  eleve = PRINCIPAL.eleve and  date=PRINCIPAL.date
GROUP BY  date,  eleve,  note) as D , 

date

FROM notes as PRINCIPAL
GROUP BY date, eleve
ORDER BY date desc

Posté : 09 févr. 2006, 15:40
par Maitrepylos
:shock: =D>

Joli

Posté : 09 févr. 2006, 15:47
par sadeq
désolé, j'ai posté 2 fois :oops:

Posté : 09 févr. 2006, 16:17
par charles59
J'hallucine :lol:

Merci à vous tous ;)

J'ai tout de même pris la solution de Hubert Roksor
Je vais essayé de venir regulierement sur ce forum pour aider un peu plus et ne pas toujours poser mes questions ;)

Posté : 09 févr. 2006, 17:27
par albat
désolé, j'ai posté 2 fois :oops:
Réparé. ;)

Posté : 10 févr. 2006, 20:55
par Hubert Roksor
Çà fait un bout de temps que je voulais réagir au post de sadeq (signé "Guest") mais je ne parviens pas à trouver le temps ou la motivation pour faire un vrai post riche et intéressant :( Pardonnez-moi de faire concis, mes intentions sont bienveillantes :)

En règle générale, lorsqu'une requête est plus longue que le résultat qu'elle produit c'est qu'il y a un problème quelque part. Malheureusement, si elle peut paraître "jolie" la requête proposée est surtout terriblement inefficace, voire erronée. En effet, un serveur tournant en sql_mode "ANSI" refusera de l'exécuter et retournera l'avertissement suivant: "Reference 'A' not supported (forward reference in item list)". C'est parce que les sous-requêtes contenues dans la partie "SELECT" se réfèrent aux valeurs des tuples examinés par la requête principale.

On pourrait toujours déplacer ces requêtes dans le "FROM", en faire des tables dérivées et les JOINdre correctement, mais cela reste très compliqué pour pas grand-chose car dans l'état actuel des choses chaque (sous-)requête (4 + 1 donc) doit faire un "full table scan" et donc examiner l'intégralité de la table. Au bout du compte on obtient les informations que l'on veut au prix d'avoir parcouru l'intégralité du fichier 5 fois d'affilée. La morale de l'histoire: KISS, plus c'est simple et mieux ça marche.

Pour info, voici le EXPLAIN qui correspond à cette requête ainsi que les warnings générés.

Code : Tout sélectionner

+----+--------------------+-----------+------+---------------+------+---------+------+------+----------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------------+-----------+------+---------------+------+---------+------+------+----------------------------------------------+ | 1 | PRIMARY | PRINCIPAL | ALL | NULL | NULL | NULL | NULL | 12 | Using temporary; Using filesort | | 5 | DEPENDENT SUBQUERY | notes | ALL | NULL | NULL | NULL | NULL | 12 | Using where; Using temporary; Using filesort | | 4 | DEPENDENT SUBQUERY | notes | ALL | NULL | NULL | NULL | NULL | 12 | Using where; Using temporary; Using filesort | | 3 | DEPENDENT SUBQUERY | notes | ALL | NULL | NULL | NULL | NULL | 12 | Using where; Using temporary; Using filesort | | 2 | DEPENDENT SUBQUERY | notes | ALL | NULL | NULL | NULL | NULL | 12 | Using where; Using temporary; Using filesort | +----+--------------------+-----------+------+---------------+------+---------+------+------+----------------------------------------------+ +-------------------------------------------------------------------------------------------+ | Warnings | +-------+------+----------------------------------------------------------------------------+ | Note | 1276 |Field or reference 'PRINCIPAL.eleve' of SELECT #2 was resolved in SELECT #1 | | Note | 1276 |Field or reference 'PRINCIPAL.date' of SELECT #2 was resolved in SELECT #1 | | Note | 1276 |Field or reference 'PRINCIPAL.eleve' of SELECT #3 was resolved in SELECT #1 | | Note | 1276 |Field or reference 'PRINCIPAL.date' of SELECT #3 was resolved in SELECT #1 | | Note | 1276 |Field or reference 'PRINCIPAL.eleve' of SELECT #4 was resolved in SELECT #1 | | Note | 1276 |Field or reference 'PRINCIPAL.date' of SELECT #4 was resolved in SELECT #1 | | Note | 1276 |Field or reference 'PRINCIPAL.eleve' of SELECT #5 was resolved in SELECT #1 | | Note | 1276 |Field or reference 'PRINCIPAL.date' of SELECT #5 was resolved in SELECT #1 | +-------+-----------------------------------------------------------------------------------+

Posté : 13 févr. 2006, 09:42
par sadeq
La requête proposée par Hubert Roksor est la plus simple et rapide, la mienne est certe longue car elle repose sur des sous-requêtes pour créer des champs, elle consomme beaucoup de ressources (multilectures indexées de la même table) mais elle reste une solution conceptuelle efficace au problème posé.

En ce qui concerne le sql_mode "ANSI" cette requête semble faillir en déclenchant le message cité par Hubert car non pas parcequ'elle ne fonctionne pas dans ce mode mais parcequ'elle utilise doubles guillemets pour les valeurs "A" , "B", "C" et "D" dans les clause WHERE des sous-requêtes. Dans ce mode il faut utiliser les quotes au lieu des guillemets pour délimiter les chaines alphanumériques.

Je cite le message : "Reference 'A' not supported (forward reference in item list)" veut dire que le moteur SQL/ANSI concidère le mot A (qui se trouve dans la sous-requête au niveau du .... WHERE note = "A" and ... ) comme un champ et non comme une valeur, champ qui ne se trouve pas dans la liste des champs de la table notes.
Ce qui est normal car pour AINSI, le nom d'un champ peut être exprimé entre guillemets mais les valeurs doivent être entre quotes.

L'environnement Mysql tournant sous le mode ANSI présente le paramètrage suivant :
  • mysql> SET GLOBAL sql_mode='ansi';
    mysql> SELECT @@global.sql_mode;
    -> 'REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,
    IGNORE_SPACE,ONLY_FULL_GROUP_BY,ANSI';
Où ANSI_QUOTES précise que les délimiteurs des chaines sont des quotes.

Pour résoudre ce problème il faut tout simplement remplacer les guillemets par les quotes dans la requête que j'ai présentée.

Ceci dit ma participation (comme d'habitude) ne ce borne pas au script proposé mais se détaille par les explications que je join avec. Ce qui vaut plus à mes yeux c'est la méthode pour résoudre le problème. :wink: