Join/Subquery 是 fast/slow 取决于我过滤的列(不是简单的索引问题)
Join/Subquery is fast/slow depending on which column I filter on (not a simple index issue)
PostgreSQL 9.3.2,由 Visual C++ build 1600 编译,64 位
每个客户可以有很多订单和推荐。现在,我想为客户创建一个包含一些统计信息的视图,其中对于每个客户,我都有一些计算列(每个客户一行)。
创建视图:
create view myview
select
a.customer_id,
sum(a.num) as num_orders,
sum(b.num) as num_referrals
from
(
select
customer.id as customer_id,
count(customer.id) as num
from
customer
left join
order
on
order.customer_id = customer.id
group by
customer.id
) a
left join
(
select
customer.id as customer_id,
count(customer.id) as num
from
customer
left join
referral
on
referral.customer_id = customer.id
group by
customer.id
) b
on
a.customer_id = b.customer_id
group by
a.customer_id,
b.customer_id
;
查询A(这个很快):
select
customer.*,
myview.*
from
customer
left join
myview
on
customer.id = myview.customer_id
where
customer.id = 100
;
查询 B(这很慢):
select
customer.*,
myview.*
from
customer
left join
myview
on
customer.id = myview.customer_id
where
customer.sex = 'M'
;
查询C(这个快):
select
customer.*,
myview.*
from
customer
left join
myview
on
customer.id = myview.customer_id
where
(select id from customer where sex = 'M')
;
好的,那么为什么查询 B 在性能方面与查询 A 有如此大的不同?我想,在查询 B 中,首先是 运行 那些子查询而没有过滤,但我不知道如何修复它。
问题是生成查询的是我们的 ORM。所以,我无法通过执行查询 C 之类的操作来解决问题。
我希望有一种更好的方法来设计我的视图来解决问题。查询 A 和查询 B 之间 EXPLAIN
结果的主要区别是查询 B 有一些 MERGE RIGHT JOIN
操作。
有什么想法吗?
编辑:
我根据评论人的要求添加了以下信息。以下是更真实的信息(与上面简化的假设场景相反)。
create or replace view myview as
select
a.id_worder,
count(a.*) as num_finance_allocations,
count(b.*) as num_task_allocations
from
(
select
woi.id_worder,
count(*) as num
from
worder_invoice woi
left join
worder_finance_task ct
on
ct.id_worder_finance = woi.id
left join
worder_finance_task_allocation cta
on
cta.id_worder_finance_task = ct.id
group by
woi.id_worder
) a
left join
(
select
wot.id_worder,
count(*) as num
from
worder_task wot
left join
worder_task_allocation wota
on
wota.id_worder_task = wot.id
group by
wot.id_worder
) b
on
a.id_worder = b.id_worder
group by
a.id_worder,
b.id_worder
;
查询 A(很快,显然我需要一个超过 10 到 post 超过 2 个链接的代表,所以这个没有解释)
select
*
from
worder a
left outer join
myview b
on
a.id = b.id_worder
where
a.id = 100
;
查询 B(慢,EXPLAIN)
select
*
from
worder a
left outer join
myview b
on
a.id = b.id_worder
where
a.id_customer = 200
查询C(快,EXPLAIN)
select
*
from
worder a
left outer join
myview b
on
a.id = b.id_worder
where
a.id = (select id from worder where id_customer = 200)
;
尝试像这样重写您的视图:
create view myview
select
c.customer_id,
(
select count(*) from order o where o.customer_id=c.customer_id
) num_orders,
(
select count(*) from referral r where r.customer_id=c.customer_id
)
from customer c ;
PostgreSQL 9.3.2,由 Visual C++ build 1600 编译,64 位
每个客户可以有很多订单和推荐。现在,我想为客户创建一个包含一些统计信息的视图,其中对于每个客户,我都有一些计算列(每个客户一行)。
创建视图:
create view myview
select
a.customer_id,
sum(a.num) as num_orders,
sum(b.num) as num_referrals
from
(
select
customer.id as customer_id,
count(customer.id) as num
from
customer
left join
order
on
order.customer_id = customer.id
group by
customer.id
) a
left join
(
select
customer.id as customer_id,
count(customer.id) as num
from
customer
left join
referral
on
referral.customer_id = customer.id
group by
customer.id
) b
on
a.customer_id = b.customer_id
group by
a.customer_id,
b.customer_id
;
查询A(这个很快):
select
customer.*,
myview.*
from
customer
left join
myview
on
customer.id = myview.customer_id
where
customer.id = 100
;
查询 B(这很慢):
select
customer.*,
myview.*
from
customer
left join
myview
on
customer.id = myview.customer_id
where
customer.sex = 'M'
;
查询C(这个快):
select
customer.*,
myview.*
from
customer
left join
myview
on
customer.id = myview.customer_id
where
(select id from customer where sex = 'M')
;
好的,那么为什么查询 B 在性能方面与查询 A 有如此大的不同?我想,在查询 B 中,首先是 运行 那些子查询而没有过滤,但我不知道如何修复它。
问题是生成查询的是我们的 ORM。所以,我无法通过执行查询 C 之类的操作来解决问题。
我希望有一种更好的方法来设计我的视图来解决问题。查询 A 和查询 B 之间 EXPLAIN
结果的主要区别是查询 B 有一些 MERGE RIGHT JOIN
操作。
有什么想法吗?
编辑:
我根据评论人的要求添加了以下信息。以下是更真实的信息(与上面简化的假设场景相反)。
create or replace view myview as
select
a.id_worder,
count(a.*) as num_finance_allocations,
count(b.*) as num_task_allocations
from
(
select
woi.id_worder,
count(*) as num
from
worder_invoice woi
left join
worder_finance_task ct
on
ct.id_worder_finance = woi.id
left join
worder_finance_task_allocation cta
on
cta.id_worder_finance_task = ct.id
group by
woi.id_worder
) a
left join
(
select
wot.id_worder,
count(*) as num
from
worder_task wot
left join
worder_task_allocation wota
on
wota.id_worder_task = wot.id
group by
wot.id_worder
) b
on
a.id_worder = b.id_worder
group by
a.id_worder,
b.id_worder
;
查询 A(很快,显然我需要一个超过 10 到 post 超过 2 个链接的代表,所以这个没有解释)
select
*
from
worder a
left outer join
myview b
on
a.id = b.id_worder
where
a.id = 100
;
查询 B(慢,EXPLAIN)
select
*
from
worder a
left outer join
myview b
on
a.id = b.id_worder
where
a.id_customer = 200
查询C(快,EXPLAIN)
select
*
from
worder a
left outer join
myview b
on
a.id = b.id_worder
where
a.id = (select id from worder where id_customer = 200)
;
尝试像这样重写您的视图:
create view myview
select
c.customer_id,
(
select count(*) from order o where o.customer_id=c.customer_id
) num_orders,
(
select count(*) from referral r where r.customer_id=c.customer_id
)
from customer c ;