Oracle:Max、Partition by,甚至是 rownum?
Oracle: Max, Partition by, or even rownum?
我来自SQL 服务器背景,所以我对Oracle 的了解很少。看起来 Partition by
在性能方面优于 max
。或者我使用 rownum
来存档我的结果 table?
我有以下 table - 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 |
请提供任何指南。如果可能,请用统计数据说明您的建议。谢谢。
EDIT: 貌似使用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 |
|------------------|------------------|-------------|------|-------------|---------------|
对于 Local
行,您需要在定义 window 分区时包括日期,因为 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,除了"ignoring"timestamp时间部分的方式不同外,在Oracle中都是一样的)
我来自SQL 服务器背景,所以我对Oracle 的了解很少。看起来 Partition by
在性能方面优于 max
。或者我使用 rownum
来存档我的结果 table?
我有以下 table - 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 |
请提供任何指南。如果可能,请用统计数据说明您的建议。谢谢。
EDIT: 貌似使用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 |
|------------------|------------------|-------------|------|-------------|---------------|
对于 Local
行,您需要在定义 window 分区时包括日期,因为 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,除了"ignoring"timestamp时间部分的方式不同外,在Oracle中都是一样的)