Help building a SQL query from multiple tables

不羁的心 提交于 2019-12-08 12:02:19

问题


Given the following tables, how might I build a SQL query that includes a list of all the items from the "items" table, and a column for each color from the "colors" table that, for each item listed, indicates what colors the item has a relationship with.

If that is unclear at all, please let me know what additional information will help clarify. The table information and desired SQL result are below:

items table:

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

colors table:

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

item_color table:

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

Desired SQL query result:

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

Thanks, Colin


回答1:


Use:

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 

Change MAX to COUNT if you want the total # of red/blue/green associated to an item.

Alternate using Subquery Factoring:

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



回答2:


Are you on oracle 11g?

This seems to be an ideal use for the new pivot feature in 11g




回答3:


If you know all the possible colours in advance, you can do it messily but effectively. If you don't know all the possible colours in advance, it is very much harder - you have to run some queries to find out which columns will appear in the result table, and then craft the SQL to create those columns (dynamic SQL).

So, let's assume you know the columns in the result table:

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

Note that in this formulation, I've used the data from the colours table in building the query. And alternative form would build the sub-queries as (inner) joins to the colours table, using the colour name instead of the code in the WHERE clauses.




回答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;

then select:

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

gives:

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

in case you don't know the list of colors beforehand you can select the color table first and then build the pivot select dynamically (a subselect like for color_name in (select color_name from color) is not possible) or alternatively you can use pivot xml and postprocess the result:

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

gives:

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>


来源:https://stackoverflow.com/questions/1363555/help-building-a-sql-query-from-multiple-tables

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!