如何改进此查询以获得更好的Oracle性能

时间:2013-03-10 03:44:41

标签: sql performance oracle oracle10g oracle11g

产品

product_id  product_serial_number   product_status
1           X123                    PENDING
1           X123                    PROCESSED
2           X345                    PENDING
3           X678                    PENDING
4           Y890                    PENDING
4           Y890                    PROCESSED

上表显示了产品的状态及其历史记录。我需要生成一个报告,其输出如下所示:

product_id  status
1           UPDATE
2           NEW
3           NEW
4           UPDATE

即。如果先前已经处理过产品(例如产品1和4),则其状态为UPDATE,否则其状态为NEW。

我已经提出了这个问题,但我对它的表现不满意:

select product_id, 'UPDATE'
from products p1
where product_id in (select product_id from products p2 where p2.product_status='PROCESSED' and p2.product_status='ARCHIVED')
Union
select product_id, 'NEW'
from products p1
where product_id not in (select product_id from products p2 where p2.product_status='PROCESSED' and p2.product_status='ARCHIVED')

另一种可行的方法是将表连接到自身:

select p1.product_id, decode(p2.product_id, null, 'NEW','UPDATE')
from products p1, products p2
where p1.product_id=p2.product_id(+)
and p1.product_serial_number=p2.serial_number(+)
and p2.product_status(+) = 'PROCESSED'

当针对大型数据集运行任一查询时,性能不是很好。我如何改进(甚至完全改变)上述查询以获得最佳性能?

4 个答案:

答案 0 :(得分:3)

您是否尝试过使用GROUP BY

SELECT product_id, (CASE WHEN COUNT(*) = 1 THEN 'NEW' ELSE 'UPDATED' END) status
FROM products
WHERE product_status <> 'ARCHIVED'
GROUP BY product_id

查看其他GROUP BY aggregate functions

修改

修复了Case expression语法的问题。对不起。

答案 1 :(得分:1)

可以使用MINUS和INTERSECT获得更好的速度,这些都是UNION的堂兄弟。

所有具有PENDING和PROCESSED行的产品:

SELECT product_id FROM Products WHERE product_status = 'PENDING'
INTERSECT SELECT product_id FROM Products WHERE product_status = 'PROCESSED'

所有具有PENDING行但不是PROCESSED行的产品:

SELECT product_id FROM Products WHERE product_status = 'PENDING'
MINUS SELECT product_id FROM Products WHERE product_status = 'PROCESSED'

将它们放在一起(并添加NEW / UPDATE):

SELECT product_id, 'NEW' FROM (
   SELECT product_id FROM Products WHERE product_status = 'PENDING'
   MINUS SELECT product_id FROM Products WHERE product_status = 'PROCESSED')
UNION
SELECT product_id, 'UPDATE' FROM (
  SELECT product_id FROM Products WHERE product_status = 'PENDING'
  INTERSECT SELECT product_id FROM Products WHERE product_status = 'PROCESSED')

对于大型表,您将至少有2/3的行涉及,因此查询永远不会超快。

如果您计划大量运行此查询,您可能还需要考虑product_status上的索引。

答案 2 :(得分:1)

试试这个

with CTE as
(
   select product_id, decode(product_status,'PROCESSED','UPDATE','NEW') status,
   row_number() over (partition by product_id
   order by decode(product_status,'PROCESSED','UPDATE','NEW') desc) rnum
   from products p1
)
select * from cte where rnum = 1

答案 3 :(得分:1)

除了之前的答案(我喜欢关于INTERSECTanñMINUS的答案)。

对于字段'product_status'的非常少量的可能值,普通索引(基于B-Tree)不能很好地工作。您需要在此字段上使用位图索引。 Oracle Bitmap Index Techniques CREATE INDEX in Oracle Docs - search for bitmap