MySql从同一行的不同列返回多行

时间:2013-02-27 17:45:04

标签: mysql sql

给出两个表:

'people'表包含以下列:

name
favorite_walking_shoe
favorite_running_shoe
favorite_dress_shoe
favorite_house_shoe
favorite_other_shoe

'shoes'表包含以下列:

shoe
name
description

我想创建一个包含以下内容的结果集:

people.name, people.favorite_shoe_type, shoes.name, shoes.description

我知道我可以使用以下内容获得所需的结果:

select p.name, p.favorite_shoe_type, s.name, s.description
  from (select name, favorite_walking_shoe as shoe, 'walking' as favorite_shoe_type
          from people where favorite_walking_shoe is not null
        union all
        select name, favorite_running_shoe, 'running'
          from people where favorite_running_shoe is not null
        union all
        select name, favorite_dress_shoe, 'dress'
          from people where favorite_dress_shoe not is null
        union all
        select name, favorite_house_shoe, 'house'
          from people where favorite_house_shoe not is null
        union all
        select name, favorite_other_shoe, 'other'
          from people where favorite_other_shoe not is null
        ) p
     join shoes s on s.shoe = p.shoe
    order by 1,2

但这需要5次传递“人员”表。有没有办法在不需要多次通过的情况下完成UNION ALL?

我应该指出,这些结构是供应商产品的一部分,我无法修改。 :(

2 个答案:

答案 0 :(得分:1)

您可以通过执行cross join来解决五次扫描:

select p.name, p.favorite_shoe_type, s.name, s.description
from (select p.*,
             (case when favorite_shoetype = 'walking' then p.favore_walking_shoe
                   when favorite_shoetype = 'running' then p.favorite_running_shoe
                   when favorite_shoetype = 'dress' then p.favorite_dress_shoe
                   when favorite_shoetype = 'house' then p.favorite_house_shoe
                   when favorite_shoetype = 'other' then p.favorite_other_shoe
              end) as shoe
      from people p cross join
           (select 'walking' as favorite_shoe_type union all
            select 'running' union all
            select 'dress' union all
            select 'house' union all
            select 'other'
           ) shoetypes join
           shoes s
     ) p
     on s.shoe = p.shoe

我不确定这会更有效率。如果鞋上有索引,这个更复杂的版本可能更有效:

select p.name, p.favorite_shoe_type, s.name, s.description
from (select p.name, favorite_shoe_types,
             (case when favorite_shoetype = 'walking' then ws.name
                   when favorite_shoetype = 'running' then rs.name
                   when favorite_shoetype = 'dress' then ds.name
                   when favorite_shoetype = 'house' then hs.name
                   when favorite_shoetype = 'other' then os.name
              end) as name,
             (case when favorite_shoetype = 'walking' then ws.description
                   when favorite_shoetype = 'running' then rs.description
                   when favorite_shoetype = 'dress' then ds.description
                   when favorite_shoetype = 'house' then hs.description
                   when favorite_shoetype = 'other' then os.name
              end) as description
      from people p left outer join
           shoes ws
           on ws.shoe = favorite_walking_shoe left outer join
           shoes rs
           on rs.shoe = favorite_running_shoe left outer join
           shoes ds
           on ds.shoe = favorite_dress_shoe left outer join
           shoes hs
           on hs.shoe = favorite_house_shoe left outer join
           shoes os
           on os.shoe = favorite_other_shoe cross join
           (select 'walking' as favorite_shoe_type union all
            select 'running' union all
            select 'dress' union all
            select 'house' union all
            select 'other'
           ) shoetypes 
     ) p
     on s.shoe = p.shoe
where s.name is not null     

这应该使用索引进行五个连接 - 非常快,一次扫描人员表,并将其提供给交叉连接。然后逻辑返回您想要的值。

注意:这两个都未经测试,因此可能会出现语法错误。

答案 1 :(得分:0)

不幸的是,您当前表的结构方式将有多个传递来获取每个值。如果可能,我建议将表结构更改为包含shoe_type表,然后在peopleshoes之间的连接表,并在此表中包含标志这将显示鞋是否是最喜欢的。

所以它将与此类似:

create table people_shoe
(
    people_id int,
    shoe_id int,
    IsFavorite int
);

您还可以使用shoe_type表来存储每种不同的节目类型:

create table shoe_type
(
    id int,
    name varchar(10)
);

insert into shoe_type
values('Walking'), ('Running'), ('Dress'), ('House'), ('Other');

shoe_type.id将被添加到您的shoe表中,您将加入表格。

编辑#1,如果你可以改造数据库,你可以使用以下(模拟模型):

create table people
(
    id int, 
    name varchar(10)
);
insert into people values (1, 'Jim'), (2, 'Jane');

create table shoe_type
(
    id int,
    name varchar(10)
);
insert into shoe_type
values(1, 'Walking'), (2, 'Running'), (3, 'Dress'), (4, 'House'), (5, 'Other');

create table shoes
(
    id int,
    name varchar(10),
    description varchar(50),
    shoe_type_id int
);
insert into shoes 
values(1, 'Nike', 'test', 2), (2, 'Cole Haan', 'blah', 3);

create table people_shoe
(
    people_id int,
    shoe_id int,
    IsFavorite int
);
insert into people_shoe
values (1, 1, 1),
(1, 2, 0),
(2, 1, 1);

然后当您查询时,您的代码将类似于:

select p.name PersonName,
  s.name ShoeName,
  st.name ShoeType,
  ps.isFavorite
from people p
inner join people_shoe ps
    on p.id = ps.people_id
inner join shoes s
    on ps.shoe_id = s.id
inner join shoe_type st
  on s.shoe_type_id = st.id

请参阅SQL Fiddle with Demo