【发布时间】:2016-11-01 21:25:16
【问题描述】:
我有 3 张表 - 一张用于用户,一张用于收款,一张用于付款。我想在一个结果集中显示所有收款和付款。我可以用多个selects 和一个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;
这是我可以用joins 做同样的事情的最接近的地方——虽然它不正确,因为我希望这个结果集看起来和以前一样,我不知道如何使用@ 987654328@:
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';
【问题讨论】:
-
在 MySQL 中没有 FULL OUTER JOIN 之类的东西 - 尽管显然您可以通过各种方式模拟一个。但是,如果该查询生成了所需的结果,那么 FULL OUTER JOIN 就不是我们想要的了。您的查询很好(虽然我不确定超级查询是否必要)
-
@Ivan:您看不到问题中已经存在的示例数据和结果?如果没有,你没有读得很透。
-
最好将过滤器
where u.name = 'bob'放在子查询中。否则它将生成一个中间表,其中所有用户都加入了他们的付款,然后它必须找到其中的所有鲍勃。 -
但除此之外,您的联合方法是正确的方法。
-
我同意巴马尔的观点。 MySQL 不会将外部查询上的谓词
name = 'bob'“推”到内联视图中。 MySQL 将实现一个派生表,其中包含bob和fred的行。在最新版本的 MySQL 之前,派生表上没有索引。并且不需要派生表。 Strawberry 是对的(除了查询应该使用UNION ALL而不是UNION),除非我们遗漏了“删除重复行”的部分规范。