MySQL使用连接查询来优化联合查询

时间:2016-06-30 00:05:01

标签: mysql join union

我有3个表 - 一个用于用户,一个用于收款,一个用于付款。我想在单个结果集中显示所有收款和付款。我可以使用多个select和一个union执行此操作,但它看起来很麻烦,我怀疑它因子查询而变慢 - 并且表格非常大(尽管我使用的是索引)。有没有更快的方法来实现这一目标?也许使用full outer join

以下是带有一些示例数据的架构的简化版本:

create table users (
  id int auto_increment,
  name varchar(20),
  primary key (id)
) engine=InnoDB;
insert into users (name) values ('bob'),('fred');

create table user_incoming_payments (
  user_id int,
  funds_in int
) engine=InnoDB;
insert into user_incoming_payments
values (1,100),(1,101),(1,102),(1,103),
(2,200),(2,201),(2,202),(2,203);

create table user_outgoing_payments (
  user_id int,
  funds_out int
) engine=InnoDB;
insert into user_outgoing_payments
values (1,100),(1,101),(2,200),(2,201);

这是一个丑陋的查询,它为用户bob生成我想要的结果:

select * from (
 (select u.name, i.funds_in, 0 as 'funds_out' from users u
 inner join user_incoming_payments i on u.id = i.user_id)
 union
 (select u.name, 0 as 'funds_in', o.funds_out from users u
 inner join user_outgoing_payments o on u.id = o.user_id)
) a where a.name = 'bob'
order by a.funds_in asc, a.funds_out asc;

这里尽可能接近join s做同样的事情 - 但这不正确,因为我希望这个结果集与之前的结果相同,我不确定如何使用full outer join

select *
from users u
right join user_incoming_payments i on u.id = i.user_id
right join user_outgoing_payments o on u.id = o.user_id
where u.name = 'bob';

SQL Fiddle here

2 个答案:

答案 0 :(得分:3)

使用这个模型,我可能会按如下方式编写该查询,但我怀疑它有多大区别......

select u.name
     , i.funds_in
     , 0 funds_out 
  from users u
  join user_incoming_payments i 
    on u.id = i.user_id
 where u.name = 'bob'
 union all
select u.name
     , 0 funds_in
     , o.funds_out 
  from users u
  join user_outgoing_payments o 
    on u.id = o.user_id
 where u.name = 'bob'
 order 
    by funds_in asc
     , funds_out asc;

但请注意,这里没有PK,这可能会有问题。

如果是我,我会有一个交易表,其中包括transaction_id PK,每个交易的时间戳,以及记录价值是信用卡还是借记卡的列。

答案 1 :(得分:3)

MySQL不支持FULL OUTER JOIN。即使它确实支持它,我也不认为你会想要它,因为它会引入半笛卡尔产品...... incoming_中的每一行都匹配outgoing_中的每一行,创建额外的行。

如果来自incoming_的四行和来自outgoing_的六行,则由连接操作生成的集合将包含24行。

这看起来更像是你想要一个集合连接操作。也就是说,您有两个要连接在一起的独立集合。这不是JOIN操作。这是 UNION ALL 设置操作。

SELECT ... FROM ... 
 UNION ALL
SELECT ... FROM ...

如果您不需要删除重复项(在这种情况下看起来您似乎不想,如果incoming_中有多个行具有相同的值{{1我不认为你想删除任何行。)...

然后使用funds_in集合运算符,执行检查和删除重复行。

UNION ALL运算符会删除重复的行。 (再次)我认为你不想要。

派生表是没有必要的。

MySQL没有"推送"从外表到内联视图的谓词。这意味着MySQL将实现一个派生表,其中包含所有用户的所有传入和传出。并且外部查询将查看该查找行。直到最新版本的MySQL,在派生表上没有创建索引。

请参阅Strawberry的答案,了解更有效的查询示例。

通过设置小的示例,索引不会有任何区别。但是,对于大型集合,您将需要添加适当的覆盖索引。

此外,对于这样的查询,我倾向于包含一个鉴别器列,告诉我哪个查询返回了一行。

UNION