我有 3 张表 - 一张用于用户,一张用于收款,一张用于收款。我想在单个结果集中显示所有收款和付款。我可以用多个 select
和一个 union
来做到这一点,但它看起来很麻烦,而且我怀疑它的速度很慢是因为子查询 - 而且表非常大(虽然我使用索引)。有没有更快的方法来实现这一目标?也许使用全外连接
?
这是带有一些示例数据的架构的简化版本:
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
做同样的事情的最接近结果——尽管它不正确,因为我希望这个结果集看起来和以前的一样,但我没有确定如何使用全外连接
:
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
。即使它确实支持它,我认为您也不会想要它,因为它会引入半笛卡尔积...... incoming_
中的每一行都与 outgoing_< 中的每一行相匹配
,创建额外的行。
如果 incoming_
有四行,outgoing_
有六行,则连接操作生成的集合将包含 24 行。
这看起来更像是您想要的集合串联操作。也就是说,您有两个要连接在一起的独立集合。这不是 JOIN
操作。这是一个 UNION ALL
集合操作。
SELECT ... FROM ...
UNION ALL
SELECT ... FROM ...
如果您不需要删除重复项(在这种情况下您似乎不想这样做,如果 incoming_
中有多行具有相同的 funds_in 值
,我认为您不想删除任何行。)...
然后使用 UNION ALL
集合运算符,它不执行检查和删除重复行。
UNION
运算符删除重复的行。哪个(再次)我认为你不想要。
派生表不是必需的。
而且 MySQL 不会将谓词从外表“推”到内联 View 中。这意味着 MySQL 将为 所有 用户实现一个包含所有传入和传出的派生表。外部查询将通过它来查找行。在 MySQL 的最新版本之前,没有在派生表上创建索引。
有关更高效查询的示例,请参阅 Strawberry 的回答。
对于小样本集,索引不会产生任何影响。但是,如果集合很大,您将需要添加适当的覆盖索引。
另外,对于这样的查询,我倾向于包含一个鉴别器列,告诉我哪个查询返回了一行。
(
SELECT 'i' AS src
, ...
FROM ...
)
UNION ALL
(
SELECT 'o' AS src
, ...
FROM ...
)
ORDER BY ...
关于MySQL 通过使用连接查询来优化联合查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38111982/