2011-08-31 211 views
1

我们在甲骨文10g快捷版下面的数据库模式: ImageOracle查询性能问题

我们的一个查询看起来是这样的:

select 
     * 
    from 
     torder_item oi_0 
    where 
     oi_0.id in 
     (
      select 
       max(oi_1.id) 
      from 
       torder_item oi_1, torder o 
      where 
       oi_1.torder_id = o.id 
      group by 
       oi_1.tproduct_id 
     ) 
     or oi_0.id in 
     (
      select 
       max(oi_2.id) 
      from 
       torder_item oi_2, tproduct p 
      where 
       oi_2.tproduct_id = p.id 
      group 
       by p.group_id 
     ); 

的问题是,在查询运行速度很慢。我目前每个表中的行数少于4000行,但查询执行时间在我的计算机上高于6秒。它是一个简化版本。如果我将'或in'更改为'union':

select 
     * 
    from 
     torder_item oi_0 
    where 
     oi_0.id in 
     ((
      select 
       max(oi_1.id) 
      from 
       torder_item oi_1, torder o 
      where 
       oi_1.torder_id = o.id 
      group by 
       oi_1.tproduct_id 
     ) 
     union 
     (
      select 
       max(oi_2.id) 
      from 
       torder_item oi_2, tproduct p 
      where 
       oi_2.tproduct_id = p.id 
      group 
       by p.group_id 
     )); 

它返回相同的结果,但立即执行。不幸的是,我们使用的Hibernate似乎不支持union,所以我不能只是像这样改变查询。这是原始查询的痕迹:

call  count  cpu elapsed  disk  query current  rows 
    ------- ------ -------- ---------- ---------- ---------- ---------- ---------- 
    Parse  1  0.04  0.14   0   10   0   0 
    Execute  1  0.00  0.00   0   0   0   0 
    Fetch  8  6.19  6.19   0  31136   0   96 
    ------- ------ -------- ---------- ---------- ---------- ---------- ---------- 
    total  10  6.24  6.34   0  31146   0   96 

    Misses in library cache during parse: 1 
    Optimizer mode: ALL_ROWS 
    Parsing user id: 5 

    Rows  Row Source Operation 
    ------- --------------------------------------------------- 
     96 FILTER (cr=31136 pr=0 pw=0 time=14041 us) 
     1111 TABLE ACCESS FULL TORDER_ITEM (cr=14 pr=0 pw=0 time=3349 us) 
     96 FILTER (cr=7777 pr=0 pw=0 time=1799577 us) 
    102096 HASH GROUP BY (cr=7777 pr=0 pw=0 time=1584153 us) 
    1234321  TABLE ACCESS FULL TORDER_ITEM (cr=7777 pr=0 pw=0 time=35809 us) 
      0 FILTER (cr=23345 pr=0 pw=0 time=4354068 us) 
     5075 HASH GROUP BY (cr=23345 pr=0 pw=0 time=4250913 us) 
    1127665  HASH JOIN (cr=23345 pr=0 pw=0 time=2716544 us) 
    1127665  TABLE ACCESS FULL TORDER_ITEM (cr=7105 pr=0 pw=0 time=38500 us) 
    3818430  TABLE ACCESS FULL TPRODUCT (cr=16240 pr=0 pw=0 time=22423 us) 

我试着添加索引并对表执行分析,但它没有帮助。

有没有人有一个想法,为什么它如此之慢,以及如何改善它?

Here is the test data if anyone wants to reproduce the problem.

+0

除了@文森特的答案。你可以用'EXISTS'重写它。 Hibernate是否允许这样做? –

+0

是的,我会考虑的。 – mlipi

+0

另一件事:你尝试在'(group_id)'''上添加'tproduct'中的索引吗?在'torder_item'表中,'(torder_ìd,id)'和'(tproduct_ìd,id)'上的索引? –

回答

5

你已经找到了解决您的性能问题。您可以使用视图并从休眠状态查询该视图。

0

我不知道Hibernate支持这种类型的EXISTS查询,但这里是如何可以写成:

select 
    * 
from 
    torder_item oi_0 
where 
    EXISTS 
    (
     select 
      * 
     from 
      torder_item oi_1, torder o 
     where 
      oi_1.torder_id = o.id 
     group by 
      oi_1.tproduct_id 
     having 
      oi_0.id = max(oi_1.id) 
    ) 
    or EXISTS 
    (
     select 
      * 
     from 
      torder_item oi_2, tproduct p 
     where 
      oi_2.tproduct_id = p.id 
     group 
      by p.group_id 
     having 
      oi_0.id = max(oi_2.id) 
    ); 
+0

不幸的是,它也是需要很长时间才能执行。我想我会尝试查看解决方案,谢谢你的回复。 – mlipi

0

按你的问题我下面的评论,我觉得这两个查询是等价于:

select 
    * 
from 
    torder_item oi_0 
where 
    oi_0.id in 
    (
     select 
      max(oi_1.id) 
     from 
      torder_item oi_1 
     group by 
      oi_1.tproduct_id 
    ) 

但是,我明白在问题中给出的查询是简化的,这可能不是真正的查询。

0

为什么这么慢?

因为对于TORDER_ITEM的每一行,Oracle执行第一个子查询,然后 - 如果oi_0.id没有发生在子查询结果中 - 第二个子查询。这就是为什么你在计划输出的“行”列中看到这么大的数字(例如3818430意味着该TPRODUCT表,其中有3762行,是全扫描1015次)

在工会的执行计划的情况下,不同的是:首先执行两个子查询并将结果(96个唯一标识符)保存在内存中,结果Oracle访问TORDER_ITEM的每一行 - 因此实际上每个子查询执行一次而不是1000次。

Don'不问问为什么优化器在第一次查询的情况下不够聪明,无法做类似的事情。

我希望Hibernate支持外连接。我的建议是将TORDER_ITEM与第一个子查询连接,然后与第二个子查询连接,并对在第一个或第二个子查询中存在某些内容的那些行进行过滤。我的意思是

SELECT oi_0.* 
    FROM torder_item oi_0 
    LEFT JOIN (SELECT MAX(oi_1.id) id 
       FROM torder_item oi_1 
      /* you don't need the join with torder here, it isn't used anyway */ 
       GROUP BY oi_1.tproduct_id 
      ) subquery1 ON subquery1.id = oi_0.id 
    LEFT JOIN (SELECT MAX(oi_2.id) id 
       FROM torder_item oi_2, 
        tproduct p 
       WHERE oi_2.tproduct_id = p.id 
       GROUP BY p.group_id 
      ) subquery2 ON subquery2.id = oi_0.id 
WHERE subquery1.id IS NOT NULL OR subquery2.id IS NOT NULL