Oracle:Max、Partition by 还是 rownum?
Posted
技术标签:
【中文标题】Oracle:Max、Partition by 还是 rownum?【英文标题】:Oracle: Max, Partition by, or even rownum? 【发布时间】:2017-05-16 09:19:53 【问题描述】:我来自 SQL Server 背景,所以我在 Oracle 方面的技能最低。看起来Partition by
在性能方面比max
好。还是我使用rownum
来归档我的结果表?
我有下表 - TableW。
| P_TYPE | TRX_DATE | PROGRAM_NO | REF_NO | SEQ_ID | Select
|-------------|----------------|------------|-----------|--------|
| 'Local' | 2016/9/5 14:37 | C1 | null | E1 | Yes (latest in Sept 5)
| 'Local' | 2016/9/5 14:36 | C1 | null | E1 |
| 'Local' | 2016/9/5 11:08 | C1 | null | E1 |
|-------------|----------------|------------|-----------|--------|
| 'Local' | 2016/9/2 15:16 | C1 | null | E1 | Yes (latest in Sept 2)
|-------------|----------------|------------|-----------|--------|
| 'Local' | 2016/9/1 15:20 | C1 | null | E1 | Yes (latest in Sept 1)
| 'Local' | 2016/9/1 14:33 | C1 | null | E1 |
|-------------|----------------|------------|-----------|--------|
| '3rd Party' | 2016/9/4 18:00 | null | D1 | E2 | Yes
| '3rd Party' | 2016/9/4 17:55 | null | D1 | E2 |
这是我想要的:
对于列 P_TYPE,如果它的值为“Local”,则使用列 PROGRAM_NO 和 SEQ_ID。否则,使用 REF_NO 和 SEQ_ID。 如果 P_TYPE 列中的值相同,请检查 TRX_DATE。如果 TRX_DATE 列指示相同的日期,则选择具有最新时间戳的日期。另一天?另一个具有最新时间戳的条目。
| P_TYPE | TRX_DATE | PROGRAM_NO | REF_NO | SEQ_ID |
|-------------|----------------|------------|-----------|--------|
| 'Local' | 2016/9/5 14:37 | C1 | null | E1 |
| 'Local' | 2016/9/2 15:16 | C1 | null | E1 |
| 'Local' | 2016/9/1 15:20 | C1 | null | E1 |
| '3rd Party' | 2016/9/4 18:00 | null | D1 | E2 |
我收到的一个脚本是在WHERE clause
中使用SELECT MAX
:
SELECT *
FROM TableW a
WHERE TRX_DATE =
CASE P_TYPE
WHEN 'Local' THEN
(SELECT MAX(TRX_DATE) FROM TableW
WHERE PROGRAM_NO = a.PROGRAM_NO AND SEQ_ID = a.SEQ_ID)
ELSE
(SELECT MAX(TRX_DATE) FROM TableW
WHERE REF_NO = a.REF_NO AND SEQ_ID = a.SEQ_ID)
END
ORDER BY TRX_DATE desc, REF_NO ASC, SEQ_ID;
它完成了这项工作。然而,通过一些研究,partition by
似乎并没有那么昂贵。参考:Tune SQL statement with max subquery
我尝试将查询重写为:
SELECT *
FROM (
SELECT *,
CASE P_TYPE
WHEN 'Local' THEN
MAX(TRX_DATE) OVER (PARTITION BY PROGRAM_NO, SEQ_ID)
ELSE
MAX(TRX_DATE) OVER (PARTITION BY REF_NO, SEQ_ID)
END AS MAX_TRX_DATE
FROM TableW
WHERE P_TYPE = 'Local'
)
WHERE TRX_DATE = MAX_TRX_DATE
但是,我只得到这个:
| P_TYPE | TRX_DATE | PROGRAM_NO | REF_NO | SEQ_ID |
|-------------|----------------|------------|-----------|--------|
| 'Local' | 2016/9/5 14:37 | C1 | null | E1 |
请提供任何指南。如果可能,请用统计数据说明您的建议。谢谢。
编辑:看起来使用row_number和partition by会大大减少执行计划甚至时间?
| CASE | OPERATION | CARDINALITY | COST | LAST CR | LAST ELAPSED |
| | | | | BUFFER GETS | TIME |
|------------------|------------------|-------------|------|-------------|---------------|
| 1 - max() in | SELECT STATEMENT | | 76 | | |
| where clause | SORT (ORDER BY) | 1 | 76 | 477 | 3602 |
|------------------|------------------|-------------|------|-------------|---------------|
| 2 - row_number | SELECT STATEMENT | | 18 | | |
| | SORT (ORDER BY) | 8 | 18 | 53 | 607 |
|------------------|------------------|-------------|------|-------------|---------------|
【问题讨论】:
【参考方案1】:对于Local
行,您需要在定义窗口分区时包括日期,因为PROGRAM_NO, REF_NO
的所有值对于这些行都是相同的:
select *
from (
SELECT *,
CASE P_TYPE
when 'Local' then
row_number() over (partition by program_no, seq_id, trunc(trx_date) order by trx_date desc)
else
row_number() over (partition by ref_no, seq_id order by trx_date desc)
end as rn
FROM TableW a
) t
where rn = 1;
在线示例:http://rextester.com/CZTY80559
(该示例使用 Postgres,但除了“忽略”时间戳的时间部分的不同方式之外,在 Oracle 中将是相同的)
【讨论】:
第二种情况是否还需要PARTITION BY
子句中的TRUNC( trx_date )
?
@MT0:我不清楚,因为样本数据没有必要的情况 - 但可能是的。
来自 OP:是的,对于第 3 方,实际数据确实显示有相同 ref_no 和 seq_id 的行在同一日期。这意味着需要在两个 PARTITION BY
子句中同时包含 TRUNC( TRX_DATE )
。以上是关于Oracle:Max、Partition by 还是 rownum?的主要内容,如果未能解决你的问题,请参考以下文章
等效于 OBIEE 中的 max() keep (partition by .. order by ..)
oracle分析函数over partition by 和group by的区别
ROW_NUMBER(), PARTITION_BY, TOP 2 MAX If MAX 第一个和最后一个位置