加入/子查询是快/慢的,具体取决于我过滤的列(不是简单的索引问题)

时间:2015-01-13 20:32:35

标签: sql performance postgresql subquery postgresql-performance

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正在生成查询。因此,我无法通过执行类似Query 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个的代表才能发布超过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)
;

1 个答案:

答案 0 :(得分:0)

尝试重写您的视图:

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 ;