【问题标题】:Help building a SQL query from multiple tables帮助从多个表构建 SQL 查询
【发布时间】:2010-11-24 17:08:58
【问题描述】:

鉴于以下表格,我如何构建一个 SQL 查询,其中包括“items”表中所有项目的列表,以及“colors”表中每种颜色的列,对于列出的每个项目,指示该项目与什么颜色有关系。

如果还不清楚,请告诉我哪些附加信息有助于澄清。表信息和所需的 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

谢谢, 科林

【问题讨论】:

    标签: sql oracle plsql oracle11g pivot


    【解决方案1】:

    用途:

    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
    

    【讨论】:

    • @rexem:我将如何对结果进行分组,以使每个 item_name 只出现一次,并且每个颜色“指示符”出现在同一行中? (以我的问题中所需的输出为例)
    • @rexem:非常感谢您的彻底回答。这产生了我所希望的。我确实必须在每个“END”和每个“MAX()”之后删除字符串周围的括号,因为我在 TOAD 中遇到错误。知道为什么吗?再次感谢您的帮助。
    • @Colin:你的意思是单引号。不知道为什么 TOAD 对列别名的语法有问题 - Management Studio 和 PL/SQL Developer 没有。
    • @rexem:我的意思是单引号 - 对此感到抱歉。奇怪的是,TOAD 的语法有问题。再次感谢。
    【解决方案2】:

    你在使用 oracle 11g 吗?

    这似乎是 11g 中新的 pivot 功能的理想用途

    【讨论】:

    • 是的,Chi - 我使用的是 Oracle 11g。
    • @Chi:你能举个例子吗?
    • @Chi:不针对问题。
    • @Chi:非常感谢您的链接。我不知道这个功能!
    【解决方案3】:

    如果你事先知道所有可能的颜色,你可以做的很乱但很有效。 如果你事先不知道所有可能的颜色,那就更难了——你必须运行一些查询来找出结果表中会出现哪些列,然后制作 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
    

    请注意,在这个公式中,我在构建查询时使用了 colours 表中的数据。另一种形式是将子查询构建为(内部)连接到颜色表,使用颜色名称而不是 WHERE 子句中的代码。

    【讨论】:

    • 如果您只需要存在性测试,而不需要计数,那么@rexem 有一个更简单有效的解决方案 - 但同样,它要求您了解完整的颜色集来构建查询。
    • 这就是pivot命令的用武之地,它可以让你在事先不知道所有可能的颜色和动态制作sql的情况下进行查询
    • @Chi:是的,PIVOT 命令(SELECT 中的子句)至少部分解决了这个问题。引用 URL 中显示的示例表示手动生成的感兴趣列的列表 - PIVOT (...for colour in ('red', 'green', 'blue')...);但是,完整手册显示您可以对 IN 列表进行子查询...
    • @Jonathan:感谢您的有用解释。
    【解决方案4】:
    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>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多