产品
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'
当针对大型数据集运行任一查询时,性能不是很好。我如何改进(甚至完全改变)上述查询以获得最佳性能?
答案 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