2009-09-01 36 views
0

给定以下表格,我如何构建一个SQL查询,其中包含“items”表中所有项目的列表以及“colors”表中每种颜色的列对于列出的每个项目,表示项目与哪些颜色有关系。帮助从多个表构建SQL查询

如果根本不清楚,请让我知道附加信息将有助于澄清。该表信息和期望的SQL结果都低于:

项表:

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

颜色表:

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

item_color表:

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

期望中的SQL查询结果:

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

感谢, 科林

回答

5

用途:

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 

更改MAX以COUNT如果你想要的关联的红/蓝/绿总#项目。

交替使用子查询分解:

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:我如何去分组结果,使每个item_name只出现一次,每个颜色“指示符”出现在同一行? (以我的问题中的所需输出为例) – 2009-09-01 18:31:56

+0

@Colin:更新了答案。 – 2009-09-01 18:43:45

+0

@rexem:非常感谢您的全面回答。这产生了我所希望的。 我不得不在每个“END”之后和每个“MAX()”之后删除字符串周围的括号,因为我在TOAD中出错。任何想法为什么? 再次感谢您的帮助。 – 2009-09-01 21:34:59

3

你在的Oracle 11g?

这似乎是pivot新功能的理想的使用在11g

+0

是,驰 - 我上的Oracle 11g。 – 2009-09-01 17:08:07

+0

@Chi:你能举一个例子吗? – 2009-09-01 17:23:55

+0

该链接有一个示例 – Chi 2009-09-01 17:35:51

2

如果你事先知道所有可能的颜色,你可以做到这一点,但乱七八糟有效。 如果您事先不知道所有可能的颜色,那么要更加困难 - 您必须运行一些查询来查找哪些列将出现在结果表中,然后制作SQL以创建这些列(动态SQL )。

所以,让我们假设你知道在结果表中的列:

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 

注意,在这一提法,我已经在建立查询中使用从颜色表中的数据。另一种形式是使用颜色名称而不是WHERE子句中的代码将子查询作为(内部)连接添加到颜色表中。

+0

如果你只需要存在测试,而不是计数,那么@rexem有一个更简单和有效的解决方案 - 但同样,要求你知道全套颜色来构建查询。 – 2009-09-01 17:12:45

+0

这就是pivot命令的来源,它可以让你在不知道所有可能的颜色的情况下进行查询,而不需要动态创建sql – Chi 2009-09-01 17:14:25

+0

@Chi:是的,PIVOT命令(SELECT中的子句)至少部分地处理了这个问题。在引用的URL中显示的示例指示了一个手动生成的感兴趣的列表 - “PIVOT(...(用于'('红','绿','蓝')...)中的颜色);然而,完整的手册显示,你可以有一个IN列表的子查询... – 2009-09-01 19:13:22

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; 

然后选择:

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') 
); 

给出:

万一
item 1 1 0 1 
item 2 0 1 1 
item 3 0 1 0 

你不知道的颜色列表事先你可以先选择颜色表,然后构建支点动态选择(像for color_name in (select color_name from color)这样的子选择是不可能的),或者可以使用pivot xml并对结果进行后处理:

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) 
) 

给出:

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>