我有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';
答案 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