2009-09-01 16 views
0

Étant donné les tableaux suivants, comment puis-je générer une requête SQL qui inclut une liste de tous les éléments de la table "items" et une colonne pour chaque couleur de la table "colors" que, pour chaque élément répertorié, indique les couleurs avec lesquelles l'élément est en relation.Création d'une requête SQL à partir de plusieurs tables

Si cela n'est pas clair du tout, s'il vous plaît laissez-moi savoir quelles informations supplémentaires aideront à clarifier. Les informations de la table et le résultat SQL souhaitées sont ci-dessous:

produits tableau:

id | item_name 
1 | 'item 1' 
2 | 'item 2' 
3 | 'item 3' 

table couleurs:

id | color_name 
1 | 'red' 
2 | 'blue' 
3 | 'green' 

table item_color:

item_id | color_id 
1  | 1 
1  | 3 
2  | 2 
2  | 3 
3  | 2 

désiré résultat de la requête SQL:

item_name | red | blue | green 
'item 1' | 1 | null | 1 
'item 2' | null| 1 | 1 
'item 3' | null| 1 | null 

Merci, Colin

Répondre

5

Utilisation:

SELECT item_name, 
     MAX(red) 'red', 
     MAX(blue) 'blue', 
     MAX(green) 'green' 
    FROM (SELECT t.item_name, 
     CASE 
      WHEN c.color_name = 'red' THEN 
      1 
      ELSE 
      NULL 
     END 'red', 
     CASE 
      WHEN c.color_name = 'blue' THEN 
      1 
      ELSE 
      NULL 
     END 'blue', 
     CASE 
      WHEN c.color_name = 'green' THEN 
      1 
      ELSE 
      NULL 
     END 'green'  
    FROM ITEMS t 
    JOIN ITEM_COLOR ic ON ic.item_id = t.item_id 
    JOIN COLORS c ON c.id = ic.color_id) 
GROUP BY item_name 

Modifier MAX compte si vous voulez que le # total de rouge/bleu/vert associé à un article.

Autre à l'aide des sous-requêtes Affacturage:

WITH icolors AS (
    SELECT t.item_name, 
      CASE 
      WHEN c.color_name = 'red' THEN 
      1 
      ELSE 
      NULL 
     END 'red', 
    CASE 
     WHEN c.color_name = 'blue' THEN 
     1 
     ELSE 
     NULL 
    END 'blue', 
    CASE 
     WHEN c.color_name = 'green' THEN 
     1 
     ELSE 
      NULL 
    END 'green'  
    FROM ITEMS t 
    JOIN ITEM_COLOR ic ON ic.item_id = t.item_id 
    JOIN COLORS c ON c.id = ic.color_id) 
    SELECT t.item_name, 
     MAX(t.red) 'red', 
     MAX(t.blue) 'blue', 
     MAX(t.green) 'green' 
    FROM icolors t 
GROUP BY t.item_name 
+0

@rexem: Comment procéder pour grouper les résultats de manière à ce que chaque nom d'article apparaisse une seule fois et que chaque "indicateur" de couleur apparaisse dans cette même ligne? (voir la sortie désirée dans ma question comme un exemple) –

+0

@Colin: Réponse mise à jour. –

+0

@rexem: Merci beaucoup pour la réponse complète. Cela produit ce que j'espérais. J'ai dû supprimer les parenthèses autour de la chaîne après chaque "END" et après chaque "MAX()", car j'obtenais des erreurs dans TOAD. Une idée de pourquoi? Merci encore pour votre aide. –

3

Êtes-vous sur 11g oracle?

Cela semble être une utilisation idéale pour la nouvelle pivot fonctionnalité dans 11g

+0

Oui, Chi - Je suis sur Oracle 11g. –

+0

@Chi: Pourriez-vous donner un exemple? –

+0

Le lien a un exemple – Chi

2

Si vous connaissez toutes les couleurs possibles à l'avance, vous pouvez le faire, mais messily efficacement. Si vous ne connaissez pas toutes les couleurs possibles à l'avance, c'est beaucoup plus difficile - vous devez exécuter des requêtes pour trouver quelles colonnes apparaîtront dans la table de résultat, puis créer le SQL pour créer ces colonnes (SQL dynamique).

Ainsi, supposons que vous connaissez les colonnes du tableau des résultats:

SELECT i.item_name, r.red, b.blue, g.green 
    FROM items i 
     LEFT JOIN 
     (SELECT item_name, COUNT(*) AS red 
      FROM item_color 
     WHERE color_id = 1 
     GROUP BY item_name) AS r 
     ON i.item_name = r.item_name 
     LEFT JOIN 
     (SELECT item_name, COUNT(*) AS green 
      FROM item_color 
     WHERE color_id = 3 
     GROUP BY item_name) AS g 
     ON i.item_name = g.item_name 
     LEFT JOIN 
     (SELECT item_name, COUNT(*) AS blue 
      FROM item_color 
     WHERE color_id = 2 
     GROUP BY item_name) AS b 
     ON i.item_name = b.item_name 

Notez que dans cette formulation, je l'ai utilisé les données de la table couleurs dans la construction de la requête. Et la forme alternative créerait les sous-requêtes en tant que jointures (internes) à la table de couleurs, en utilisant le nom de couleur au lieu du code dans les clauses WHERE.

+0

Si vous avez seulement besoin de tests d'existence, pas de compte, alors @rexem a une solution plus simple et efficace - mais encore une fois, vous devez connaître l'ensemble des couleurs pour construire la requête. –

+0

c'est là que la commande pivot arrive, elle vous permet de faire la requête sans connaître toutes les couleurs possibles à l'avance et sans créer dynamiquement sql – Chi

+0

@Chi: oui, la commande PIVOT (clause dans SELECT) s'occupe au moins en partie de ça. L'exemple montré à l'URL référencée indique une liste générée manuellement des colonnes intéressées - 'PIVOT (... pour la couleur dans ('rouge', 'vert', 'bleu') ...)'; cependant, le manuel complet montre que vous pouvez avoir une sous-requête pour la liste IN ... –

1
create table item (id number not null, item_name varchar2(200) not null); 
create table color (id number not null, color_name varchar2(200) not null); 
create table item_color (item_id number not null, color_id number not null); 

insert into item values (1, 'item 1'); 
insert into item values (2, 'item 2'); 
insert into item values (3, 'item 3'); 


insert into color values (1, 'red'); 
insert into color values (2, 'blue'); 
insert into color values (3, 'green'); 

insert into item_color values (1, 1); 
insert into item_color values (1, 3); 
insert into item_color values (2, 2); 
insert into item_color values (2, 3); 
insert into item_color values (3, 2); 

commit; 

puis sélectionnez:

select * from 
(
select 
    i.item_name 
    , c.color_name 
from 
    item i 
    , color c 
    , item_color ic 
where 
    ic.item_id = i.id 
    and ic.color_id = c.id 
) pivot (
    count(color_name) cnt 
    for color_name in ('red', 'blue', 'green') 
); 

donne:

item 1 1 0 1 
item 2 0 1 1 
item 3 0 1 0 

au cas où vous ne connaissez pas la liste des couleurs à l'avance, vous pouvez sélectionner la table de couleurs d'abord, puis construire le pivot sélectionnez dynamiquement (une sous-sélection comme for color_name in (select color_name from color) n'est pas possible) ou bien vous pouvez utiliser pivot xml et post-traiter le résultat:

select * from 
(
select 
    i.item_name 
    , c.color_name 
from 
    item i 
    , color c 
    , item_color ic 
where 
    ic.item_id = i.id 
    and ic.color_id = c.id 
) pivot xml (
    count(color_name) cnt 
    for color_name in (any) 
) 

donne:

item 1 <PivotSet><item><column name = "COLOR_NAME">green</column><column name = "CNT">1</column></item><item><column name = "COLOR_NAME">red</column><column name = "CNT">1</column></item></PivotSet> 
item 2 <PivotSet><item><column name = "COLOR_NAME">blue</column><column name = "CNT">1</column></item><item><column name = "COLOR_NAME">green</column><column name = "CNT">1</column></item></PivotSet> 
item 3 <PivotSet><item><column name = "COLOR_NAME">blue</column><column name = "CNT">1</column></item></PivotSet> 
Questions connexes