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中都是一样的)