Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage SQL Discussion :

Résumer 2 colonnes en une seule


Sujet :

Langage SQL

  1. #1
    Candidat au Club
    Résumer 2 colonnes en une seule
    Bonjour à tous,

    L'intitulé du sujet n'est pas très clair, mais j'espère que cela deviendra plus compréhensible avec l'explication qui va suivre.

    Je galère un peu sur une requête SQL.

    J'ai 5 tables :
    - Cable (id_cable)
    - Cable_patch(id_cable, id_chambre, id_boite))
    - cassette (id_cassette, id_chambre)
    - tiroir (id_tiroir, id_boite)
    - Position (id_position,id_tiroir, id_cassette)

    Et voici ma requête :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
     
    SELECT
        cable.id_cable AS id_cable,
        COALESCE (F3.nb_fibre3, 0) AS nb_position_cassette,
        COALESCE ( F4.nb_fibre4,0) AS nb_position_boite
    FROM
        cable 
            LEFT JOIN (
    		SELECT
    		    cable.id_cable,
    		    COUNT(*) nb_fibre3
    		FROM 
    		    cable
    		        INNER JOIN cable_patch
    		            ON cable.id_cable = cable_patch.id_cable
    		        LEFT JOIN cassette 
    		            ON cable_patch.id_chambre = cassette.id_chambre
    		        LEFT JOIN position
    		            ON cassette.id_cassette =  position.id_cassette
    		GROUP BY cable.id_cable
    	    ) F3
    	    ON cable.id_cable = F3.id_cable
    	LEFT JOIN (
    		SELECT
    		    cable.id_cable,
    		    COUNT (*) nb_fibre4
    		FROM cable
    		    INNER JOIN cable_patch
    		        ON cable.id_cable = cable_patch.id_cable
    		    LEFT JOIN tiroir 
    		        ON  cable_patch.id_boite = t_tiroir.id_boite
    		    LEFT JOIN position 
    		        ON t_tiroir.id_tiroir = position.id_tiroir
    		    GROUP BY cable.id_cable
    	    ) F4
    	    ON cable.id_cable = F4.id_cable

    Cette requête est divisée en 2 "sous-requête", nommée F3 et F4.
    La requête F3 va me donner, pour chaque câble, le nombre de "position" possédant un "id_cassette" (en passant par plusieurs jointures : cable vers cable_patch, cable_patch vers cassette, et cassette vers position)
    La requête F4 va me donner, pour chaque câble, le nombre de "position" possédant un "id_tiroir" (en passant par plusieurs jointures : cable vers cable_patch, cable_patch vers tiroir, et tiroir vers position)
    Du coup, le résultat de ma requête ressemblera à quelque chose comme ça :



    Ce que j'aimerais faire, c'est donc résumer les champs "chambre_positif" et "boite_positif" en une seule colonne :
    Chaque câble ne peut pas avoir à la fois des positions possédant un "id_cassette" et un "id_tiroir". Et pour savoir si un câble va avoir des positions possédant un "id_cassette" ou bien un "id_tiroir", il faut aller voir la table "cable_patch".
    Cette table comporte les identifiants de chaque câbles, ainsi que les champs "id_chambre" et "id_boite".

    A partir de là, il n'y a que 2 possibilités :
    - si, dans la table "cable_patch", un câble possède une valeur "id_chambre", ce câble aura des positions de type "id_cassette" que l'on peut récupérer avec les jointures présentes dans ma requête F3.
    - si, dans la table "cable_patch", un câble possède une valeur "id_boite", ce câble aura des positions de type "id_tiroir" que l'on peut récupérer avec les jointures présentes dans ma requête F4.

    J'aimerais donc avoir une seule colonne qui me donnerait, pour chaque câble, le nombre de "position" de type "id_cassette" (en allant donc voir si ce câble possède une valeur "id_chambre" dans la table "cable_patch")
    OU
    qui me donnerait, pour un câble ne possédant aucune valeur "id_chambre" dans "cable_patch", le nombre de "position" de type "id_tiroir" (en allant donc voir si ce câble possède une valeur "id_boite" dans la table "cable_patch").

    J'espère que toutes ces explications ne sont pas trop longue et/ou trop compliquées. Pour résumé, j'aimerais juste transformer mes 2 "sous-requêtes" F3 et F4 qui me donnent deux champs différents en une seule requête qui me donnerait un seul champs.

    Merci d'avance à ceux qui me liront, et n'hésitez pas à me dire si mon problème n'est pas suffisamment clair

  2. #2
    Modérateur

    Essayez quelque chose comme ceci :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
        select cb.id_cable
             , greatest(count(pc.id_cassette), count(pt.id_tiroir)) as nb_position
          from cable       as cb
    inner join cable_patch as cp on cp.id_cable    = cb.id_cable
     left join cassette    as cs
    inner join position    as pc on pc.id_cassette = cs.id_cassette
                                 on cs.id_chambre  = cp.id_chambre
     left join tiroir      as tr
    inner join position    as pt on pt.id_tiroir   = tr.id_tiroir
                                 on tr.id_boite    = cp.id_boite
      group by cb.id_cable;

  3. #3
    Expert confirmé
    Juste une question Waldar, quand tu écris :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
        select ...
          from cable       as cb
    inner join cable_patch as cp on cp.id_cable    = cb.id_cable
     left join cassette    as cs
    inner join position    as pc on pc.id_cassette = cs.id_cassette
                                 on cs.id_chambre  = cp.id_chambre


    C'est la même chose qu'avec sous-requête ?
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
        select ...
          from cable       as cb
    inner join cable_patch as cp on cp.id_cable    = cb.id_cable
     left join (select cs.id_chambre, ...
                  from cassette as cs
            inner join position as pc on pc.id_cassette = cs.id_cassette 
               ) t
            on t.id_chambre  = cp.id_chambre


    Cette syntaxe est-elle implémentée sur tous les SGBD ?

  4. #4
    Modérateur

    bonjour,
    Citation Envoyé par skuatamad Voir le message

    C'est la même chose qu'avec sous-requête ?
    oui

    Citation Envoyé par skuatamad Voir le message

    Cette syntaxe est-elle implémentée sur tous les SGBD ?
    au moins les principaux :
    Oracle, SQL Server, Mysql (attention, sous mysql, s'il manque le 2eme ON, un JOIN devient un CROSS JOIN !) , et PG aussi il me semble.

  5. #5
    Expert confirmé
    Merci aieeeuuuuu

  6. #6
    Candidat au Club
    Un grand merci pour ta réponse, ça marche nickel

  7. #7
    Modérateur

    Citation Envoyé par skuatamad Voir le message
    Juste une question Waldar, quand tu écris :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
        select ...
          from cable       as cb
    inner join cable_patch as cp on cp.id_cable    = cb.id_cable
     left join cassette    as cs
    inner join position    as pc on pc.id_cassette = cs.id_cassette
                                 on cs.id_chambre  = cp.id_chambre

    Comme l'a déjà dit aieeeuuuuu oui, mais je préfère plutôt le visualiser comme ceci :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
        select ...
          from cable       as cb
    inner join cable_patch as cp on cp.id_cable    = cb.id_cable
     left join (cassette   as ct
    inner join  position   as pc on pc.id_cassette = ct.id_cassette) as cs
                                 on cs.id_chambre  = cp.id_chambre

  8. #8
    Candidat au Club
    Waldar, j'aurais une petite question par rapport à ta requête :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    select cb.id_cable
             , greatest(count(pc.id_cassette), count(pt.id_tiroir)) as nb_position
          from cable       as cb
    inner join cable_patch as cp on cp.id_cable    = cb.id_cable
     left join cassette    as cs
    inner join position    as pc on pc.id_cassette = cs.id_cassette
                                 on cs.id_chambre  = cp.id_chambre
     left join tiroir      as tr
    inner join position    as pt on pt.id_tiroir   = tr.id_tiroir
                                 on tr.id_boite    = cp.id_boite
      group by cb.id_cable;


    Ma table "position" possède également un champs "fonction".
    Si je veux, par exemple, filtrer les résultats de cette requête en n'ayant que les positions dont la fonction est 'A', comment dois-je faire ?
    Ce qui me perturbe, c'est que la requête utilise la table position 2 fois, et lui donne donc 2 alias différents.
    Je me doute bien qu'utiliser " WHERE pc.fonction = 'A' " ou " WHERE pt.fonction = 'A' " juste avant le GROUP BY me donnera des résultats différents, puisque le filtre ne se fera pas sur la totalité des positions.
    Mais d'un autre côté, " WHERE pc.fonction = 'A' AND pt.fonction = 'A' " ne me donne aucun résultat

  9. #9
    Modérateur

    Oui, c'est un "effet de bord" des jointures externes, si vous filtrez dans le WHERE vous faites disparaître l'aspect externe de la jointure (car tous les nulls seront exclus du filtre).
    Vous pouvez faire comme ceci :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
      select cb.id_cable
             , greatest(count(pc.id_cassette), count(pt.id_tiroir)) as nb_position
          from cable       as cb
    inner join cable_patch as cp  on cp.id_cable    = cb.id_cable
     left join cassette    as cs
    inner join position    as pc  on pc.id_cassette = cs.id_cassette
                                 and pc.fonction    = 'A'
                                  on cs.id_chambre  = cp.id_chambre
     left join tiroir      as tr
    inner join position    as pt  on pt.id_tiroir   = tr.id_tiroir
                                 and pt.fonction    = 'A'
                                  on tr.id_boite    = cp.id_boite
      group by cb.id_cable;

  10. #10
    Candidat au Club
    Merci pour ta réponse.
    En effet, filtrer de cette façon marche très bien. Jusque-là, je n'avais jamais dû filtrer autrement qu'avec WHERE, du coup je vais essayer de garder cet effet de bord en tête.
    Encore merci pour ton aide

###raw>template_hook.ano_emploi###