Précédent   Forum des professionnels en informatique > Bases de données > Langage SQL
Langage SQL Forum d'entraide sur le langage SQL et sur les questions liées à la conception de schéma (DDL). Cours SQL
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 31/08/2011, 10h13   #1
Membre à l'essai
 
Inscription : novembre 2009
Messages : 91
Détails du profil
Informations forums :
Inscription : novembre 2009
Messages : 91
Points : 20
Points : 20
Par défaut Lignes vers colonnes

Bonjour à tous,

J'ai un problème simple et classique, mais dont je ne trouve pas la solution.

Il s'agit d'un projet scientifique à vocation statistique et visant une certaine souplesse d'exploitation, ce qui explique que je stocke mes données de manière peu orthodoxe.

Pour faire court, j'ai une table data de type :
Code :
1
2
3
4
5
6
7
8
id_individu | id_variable | valeur
==================================
1           | 1           | "rouge"
1           | 2           | 2
1           | 3           | "beaucoup"
2           | 1           | "bleu"
2           | 2           | 3
2           | 3           | "un peu"
etc. (associées à deux autres tables, l'une pour les individus, l'autre les variables).

et je veux créer une requête me permettant de "recolonniser" (dsl) les données en tableau de type "excel", comme suit :
Code :
1
2
3
4
id_individu | var1    | var2 | var3
==================================
1           | "rouge" | 2    | "beaucoup"
2           | "bleu"  | 3    | "un peu"
J'ai commencé par utiliser des sous requêtes :
Code :
1
2
3
4
5
SELECT id_individu, 
(SELECT value FROM `data` WHERE id_var = '1') AS var1,
(SELECT value FROM `data` WHERE id_var = '2') AS var2,
(SELECT value FROM `data` WHERE id_var = '3') AS var3
FROM `data`
Ça marche. Sauf qu'avec 300 variables et 700 individus pour plus de 50000 lignes, la bécane s'étouffe à chaque coup (déjà au-delà de 10, c'est dur...). Il me faut donc une autre méthode.

J'ai songé aux RIGHT OUTER JOIN, et à utiliser des conditions (IF (id_var = '1', value, ''), mais ça répond pas au problème.

Je cherche encore mais si l'un d'entre vous connaît la solution ou est prêt à donner une piste, je suis preneur...

Merci d'avance et bonne journée,

==> Je précise : je suis sous MySQL 5.0.
Chello est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/08/2011, 10h18   #2
Modérateur
 
Avatar de CinePhil
 
Homme Philippe Leménager
Ingénieur d'études en informatique
Inscription : août 2006
Messages : 11 029
Détails du profil
Informations personnelles :
Nom : Homme Philippe Leménager
Âge : 48
Localisation : France, Haute Garonne (Midi Pyrénées)

Informations professionnelles :
Activité : Ingénieur d'études en informatique
Secteur : Enseignement

Informations forums :
Inscription : août 2006
Messages : 11 029
Points : 18 327
Points : 18 327
Envoyer un message via MSN à CinePhil
Ce que tu cherches à faire est de la cosmétique et n'est pas le boulot du SGBD mais celui du programme qui présente les données à l'utilisateur. Tu as d'ailleurs découvert toi-même les limites de ce bricolage qui s'avère irréaliste au delà de quelques valeurs !
__________________
Philippe Leménager. Ingénieur d'étude à l'École Nationale de Formation Agronomique.
Mon blog sur la conception des BDD, le langage SQL, le PHP avec Zend Framework...
« Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
À la maison comme au bureau, j'utilise Mandriva Linux ou Mageïa ! Soutenons l'industrie logicielle française !
Linuxiens, comptez-vous !
CinePhil est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/08/2011, 10h18   #3
Expert Confirmé
 
Homme
Inscription : mai 2002
Messages : 1 641
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 29
Localisation : France, Rhône (Rhône Alpes)

Informations forums :
Inscription : mai 2002
Messages : 1 641
Points : 2 634
Points : 2 634
Bonjour,

sous MySql vous avez une fonction group_concat qui correspond à peu prés à votre demande.

sinon ce que vous essayez de faire n'étant pas vraiment orthodoxe c'est normale que mysql ait du mal.

Ceci devrait être géré par un programme qui génère votre rapport.
punkoff est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/08/2011, 10h38   #4
Membre à l'essai
 
Inscription : novembre 2009
Messages : 91
Détails du profil
Informations forums :
Inscription : novembre 2009
Messages : 91
Points : 20
Points : 20
Merci pour vos réponses éclairs.

Effectivement, j'aurais dû préciser que la programmation (php) était la voie initiale et privilégiée pour résoudre à ce problème.
Problème : là aussi, ça patine et j'ai des soucis de mémoire : le script bouffe 135 Mo et je n'ai pas encore mis la main sur le bon paramètre pour élargir la mémoire (alors que le memory_limit est de 256 Mo dans php.ini).

Par conséquent, comme on me demande à titre urgent les données provisoires sous format csv, j'essaie de passer par une requête manuelle exécutée sous phpMyAdmin.

J'ai aussi songé à GROUP_CONCAT, mais comme toutes les variables ne sont pas renseignées (et dans le désordre), je risque d'avoir de sérieux décalages.

edit : Woops, il bouffe 134217728 bytes, i.e. 128 Mo. Pourtant, j'ai modifié le plafond...
Chello est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/08/2011, 10h52   #5
Membre émérite
 
Inscription : août 2008
Messages : 835
Détails du profil
Informations forums :
Inscription : août 2008
Messages : 835
Points : 823
Points : 823
Bonjour,

Essayez avec un CASE :
Code :
1
2
3
4
5
6
7
SELECT 
       id_individu, 
       max (CASE WHEN id_variable = 1 THEN valeur ELSE NULL END) AS var1,
       max (CASE WHEN id_variable = 2 THEN valeur ELSE NULL END) AS var2,
       max (CASE WHEN id_variable = 3 THEN valeur ELSE NULL END) AS var3
FROM DATA
GROUP BY id_individu
Snipah est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/08/2011, 10h56   #6
Modérateur
 
Avatar de CinePhil
 
Homme Philippe Leménager
Ingénieur d'études en informatique
Inscription : août 2006
Messages : 11 029
Détails du profil
Informations personnelles :
Nom : Homme Philippe Leménager
Âge : 48
Localisation : France, Haute Garonne (Midi Pyrénées)

Informations professionnelles :
Activité : Ingénieur d'études en informatique
Secteur : Enseignement

Informations forums :
Inscription : août 2006
Messages : 11 029
Points : 18 327
Points : 18 327
Envoyer un message via MSN à CinePhil
Citation:
Envoyé par Chello Voir le message
Effectivement, j'aurais dû préciser que la programmation (php) était la voie initiale et privilégiée pour résoudre à ce problème.
Problème : là aussi, ça patine et j'ai des soucis de mémoire : le script bouffe 135 Mo et je n'ai pas encore mis la main sur le bon paramètre pour élargir la mémoire (alors que le memory_limit est de 256 Mo dans php.ini).
Quel est l'algorithme de ce script ?

Citation:
Par conséquent, comme on me demande à titre urgent les données provisoires sous format csv, j'essaie de passer par une requête manuelle exécutée sous phpMyAdmin.
Ben tu expliqueras au demandeur que ce n'est pas comme ça qu'on travaille avec un SGBD et qu'en plus MySQL n'est pas constitué pour travailler ainsi, notamment parce qu'il ne connaît pas les requêtes récursives.

Citation:
J'ai aussi songé à GROUP_CONCAT, mais comme toutes les variables ne sont pas renseignées (et dans le désordre), je risque d'avoir de sérieux décalages.
De toutes façon, GROUP_CONCAT te mettrait toutes les valeurs en chaîne dans une seule colonne : " 'rouge', 2, 'beaucoup' "
__________________
Philippe Leménager. Ingénieur d'étude à l'École Nationale de Formation Agronomique.
Mon blog sur la conception des BDD, le langage SQL, le PHP avec Zend Framework...
« Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
À la maison comme au bureau, j'utilise Mandriva Linux ou Mageïa ! Soutenons l'industrie logicielle française !
Linuxiens, comptez-vous !
CinePhil est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/08/2011, 11h12   #7
Membre à l'essai
 
Inscription : novembre 2009
Messages : 91
Détails du profil
Informations forums :
Inscription : novembre 2009
Messages : 91
Points : 20
Points : 20
@CinePhil
Le script stocke dans un tableau toutes les données sous forme d'objets (objet de classe variable, objet de classe individu, objet de classe data). C'est lui qui bouffe tout.

Par ailleurs, ayant conçu le système, je suis engagé sur le résultat. Ça fonctionnait bien avec quelques centaines d'individu sur 50 variables. C'est mon premier "gros" projet. J'optimise au fur et à mesure.

@Snipah
Je viens d'essayer CASE à l'arrache. Ça pourrait peut-être fonctionner. Je creuse ça en début d'apm. Merci !
Chello est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/08/2011, 11h29   #8
Expert Confirmé
 
Homme
Inscription : mai 2002
Messages : 1 641
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 29
Localisation : France, Rhône (Rhône Alpes)

Informations forums :
Inscription : mai 2002
Messages : 1 641
Points : 2 634
Points : 2 634
Si vous ramenez toute votre BDD en mémoire via des objets c'est sur que ça va prendre des ressources.

Là vous essayez de faire un csv (ou xls), pourquoi ne pas le générer directement avec la sortie de votre requête sql ?
Si elle est ordonnée ca devrait être assez simple de traiter ligne par ligne le résultat.
punkoff est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/08/2011, 11h48   #9
Modérateur
 
Avatar de CinePhil
 
Homme Philippe Leménager
Ingénieur d'études en informatique
Inscription : août 2006
Messages : 11 029
Détails du profil
Informations personnelles :
Nom : Homme Philippe Leménager
Âge : 48
Localisation : France, Haute Garonne (Midi Pyrénées)

Informations professionnelles :
Activité : Ingénieur d'études en informatique
Secteur : Enseignement

Informations forums :
Inscription : août 2006
Messages : 11 029
Points : 18 327
Points : 18 327
Envoyer un message via MSN à CinePhil
Si c'est seulement l'id_individu qui vous intéresse, ramenez toute votre table data ordonnée par individu et par variable dans un tableau PHP puis gérez les ruptures de personnes et de variables dans la présentation des données. C'est assez classique à faire en PHP et avec 50 000 lignes, ça ne devrait pas poser de gros problèmes.

Si vous voulez le nom des individus et le nom des variables, voire d'autres infos complémentaires, faites une jointure de votre table data avec les tables individus et variables.
Code :
1
2
3
4
5
6
7
SELECT i.nom_individu AS individu,
    v.nom_variable AS variable,
    d.valeur
FROM DATA d
INNER JOIN individu i ON i.id_individu = d.id_individu
INNER JOIN variable v ON v.id_variable = d.id_variable
ORDER BY i.nom_individu, v.nom_variable
Vous pouvez d'abord extraire tous les noms de variables pour construire votre tableau de présentation de données. En supposant que la table variable existe et que chaque variable a un nom unique :
Code :
1
2
3
SELECT nom_variable
FROM variable
ORDER BY nom_variable
Avec ça, vous pouvez déjà construire l'entête du tableau de présentation des données. 300 variables, je n'aimerais pas être à la place de celui qui lira les données à l'écran !

Ensuite, dans une boucle parcourant le tableau issu de la première requête, vous gérer les changement d'individus pour construire les lignes et, à l'intérieur de chaque ligne, vous comparez la liste des variables de l'individu à la liste de toutes les variables pour gérer l'affichage de la valeur ou rien si la variable n'a pas de valeur pour l'individu considéré.

Mais tout ceci est plutôt à voir dans le forum PHP.
__________________
Philippe Leménager. Ingénieur d'étude à l'École Nationale de Formation Agronomique.
Mon blog sur la conception des BDD, le langage SQL, le PHP avec Zend Framework...
« Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
À la maison comme au bureau, j'utilise Mandriva Linux ou Mageïa ! Soutenons l'industrie logicielle française !
Linuxiens, comptez-vous !
CinePhil est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/08/2011, 16h21   #10
Membre à l'essai
 
Inscription : novembre 2009
Messages : 91
Détails du profil
Informations forums :
Inscription : novembre 2009
Messages : 91
Points : 20
Points : 20
Bon, l'opérateur CASE ne convient pas. Comme je commence tout juste les opérateurs SQL, je crois comprendre que c'est logique : après le premier cas positif, il sort de la boucle. Par conséquent, j'ai toujours une colonne renseignée, mais les deux autres (sur un test avec trois colonnes) sont systématiquement nulles.

Voici ce que j'ai essayé :
Code :
1
2
3
4
5
6
SELECT id_individu, 
       (CASE WHEN DATA.id_var = '1042' THEN value ELSE NULL END) AS var1,
       (CASE WHEN DATA.id_var = '1043' THEN value ELSE NULL END) AS var2,
       (CASE WHEN DATA.id_var = '1045' THEN value ELSE NULL END) AS var3
FROM `data`
GROUP BY id_individu
J'ai basculé sur IF() :
Code :
1
2
3
4
5
6
SELECT id_individu 
       IF(DATA.id_var = '1042', value, NULL) AS var1,
       IF(DATA.id_var = '1043', value, NULL) AS var2,
       IF(DATA.id_var = '1044', value, NULL) AS var3
FROM `data`
GROUP BY id_individu
Résultat rigoureusement équivalent. Par contre, l'éditeur me fait ressortir les IF en rouge. Pourtant, je crois pas qu'il y ait d'erreur.

Le problème, c'est GROUP BY. Il ne remplace pas les champs NULL par les champs non NULL ; il ne fait que conserver la première ligne... C'est bête, ça...

@CinePhil
Effectivement, je crois que ça va se terminer comme ça, avec un traitement php d'une requête. Je perds pas mal de fonctions dans l'affaire (contrôle et versions multiples des mêmes données), mais j'essaierai de rectifier avec le temps.
Chello est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/08/2011, 16h25   #11
Membre émérite
 
Inscription : août 2008
Messages : 835
Détails du profil
Informations forums :
Inscription : août 2008
Messages : 835
Points : 823
Points : 823
Parce que vous avez enlevé les max, regardez à nouveau mon exemple, vous devriez obtenir ce que vous voulez.

Edit : d'ailleurs un autre SGBD que MySQL vous aurait interdit de faire une telle requête. Du coup il ne conserve qu'une ligne "au hasard", donc forcément pour chaque id_individu une seul variable est renseignée.
Snipah est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/08/2011, 17h00   #12
Membre à l'essai
 
Inscription : novembre 2009
Messages : 91
Détails du profil
Informations forums :
Inscription : novembre 2009
Messages : 91
Points : 20
Points : 20
HouUUUUuu pinaise !

Merci Snipah !

J'avais bien mis le max() au début, mais il se plaignait de ne pas trouver la fonction. J'avais donc laissé tomber et retirer la fonction, ce qui semblait encore... fonctionner.
Avec MAX en majuscule, ça marche !

Et je comprends maintenant l'utilité de MAX() : si supérieur ou différent de NULL, alors je garde. Sinon, je jette.

Hurrah !! Parce qu'autrement, même en passant par un simple tableau php pour traiter une requête SQL "en ligne", toute ma mémoire y passe... !
Chello est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/08/2011, 17h41   #13
Membre émérite
 
Inscription : août 2008
Messages : 835
Détails du profil
Informations forums :
Inscription : août 2008
Messages : 835
Points : 823
Points : 823
Citation:
Et je comprends maintenant l'utilité de MAX() : si supérieur ou différent de NULL, alors je garde. Sinon, je jette.
Pas vraiment, c'est une fonction d'agrégation qui conserve la valeur maximum de l'expression placée entre parenthèse pour le groupement demandé.

Si vous avez un peu de temps pour vous renseigner, vous pouvez lire par exemple le cours de sqlpro : http://sqlpro.developpez.com/cours/sqlaz/ensembles/
Snipah est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/08/2011, 18h05   #14
Membre à l'essai
 
Inscription : novembre 2009
Messages : 91
Détails du profil
Informations forums :
Inscription : novembre 2009
Messages : 91
Points : 20
Points : 20
Je découvre en effet pas mal de choses. C'est bon pour la suite.

Merci encore !
Chello est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité Cette discussion est résolue.
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 21h35.


 
 
 
 
Partenaires

Hébergement Web