在PARTITION BY函数中,ORDER BY的作用是什么?

时间:2018-08-09 07:47:19

标签: sql oracle sql-order-by partition-by

我有一张桌子,后面跟着数据,

    ID        SEQ    EFFDAT                 
------- ---------    -----------------------
  1024          1    01/07/2010 12:00:00 AM   
  1024          3    18/04/2017 12:00:00 AM   
  1024          2    01/08/2017 12:00:00 AM   

当我执行以下查询时,我得到了错误的最大序列,但仍然得到了正确的最大生效日期。

查询:

SELECT 
max(seq) over (partition by id order by EFFDAT desc) maxEffSeq,
partitionByTest.*,
max(EFFDAT) over (partition by (id) order by EFFDAT desc ) maxeffdat
FROM partitionByTest;

输出:

 MAXEFFSEQ         ID        SEQ EFFDAT                   MAXEFFDAT              
---------- ---------- ---------- ------------------------ ------------------------
         2       1024          2 01/08/2017 12:00:00 AM   01/08/2017 12:00:00 AM   
         3       1024          3 18/04/2017 12:00:00 AM   01/08/2017 12:00:00 AM   
         3       1024          1 01/07/2010 12:00:00 AM   01/08/2017 12:00:00 AM 

如果我在查询中删除订单,则输出正确。

查询:

SELECT max(seq) over (partition by id ) maxEffSeq, partitionByTest.*,
max(EFFDAT) over (partition by (id) order by EFFDAT desc ) maxeffdat
FROM partitionByTest;

输出:

 MAXEFFSEQ         ID        SEQ EFFDAT                   MAXEFFDAT              
---------- ---------- ---------- ------------------------ ------------------------
         3       1024          2 01/08/2017 12:00:00 AM   01/08/2017 12:00:00 AM   
         3       1024          3 18/04/2017 12:00:00 AM   01/08/2017 12:00:00 AM   
         3       1024          1 01/07/2010 12:00:00 AM   01/08/2017 12:00:00 AM   

我知道当我们使用MAX函数时,不需要使用order by子句。但是我很想知道order by如何在按功能划分中起作用,为什么当我使用order by子句时为什么给出错误的序列结果和正确的日期结果?

2 个答案:

答案 0 :(得分:7)

添加order by也意味着有一个窗口条款,并且由于您没有指定默认值,所以您实际上是在这样做:

max(seq) over (
  partition by id
  order by EFFDAT desc
  range between unbounded preceding and current row
)

如果考虑以相同的方式对数据进行排序(按降序排列),则该数据的外观:

select partitionbytest.*,
  count(*) over (partition by id order by effdat desc) range_rows,
  max(seq) over (partition by id order by effdat desc) range_max_seq,
  count(*) over (partition by id) id_rows,
  max(seq) over (partition by id) id_max_seq
from partitionbytest
order by effdat desc;

        ID        SEQ EFFDAT     RANGE_ROWS RANGE_MAX_SEQ    ID_ROWS ID_MAX_SEQ
---------- ---------- ---------- ---------- ------------- ---------- ----------
      1024          2 2017-08-01          1             2          3          3
      1024          3 2017-04-18          2             3          3          3
      1024          1 2010-07-01          3             3          3          3

然后它会变得更加清晰。我已经包括了等效的分析计数,因此您也可以查看考虑了多少行(带有和不带有order by子句)。

  • 对于第一行,最大seq值是通过查看该当前行的数据以及所有具有较晚日期(因为它是降序的)的先前行来发现的,没有一个,因此它是该值行本身-就是2。不考虑其后的seq值为3和1的行。

  • 对于第二行,它查看当前行以及所有之前的行以及后面的日期,因此它可以考虑先前的值2和当前的值3。由于其中3最高,因此显示那。紧随其后的行,其seq值为1。

  • 对于第三行,它查看当前行和所有前面的行,以及后面的日期,因此它可以考虑前面的2和3以及当前值1。由于3仍然是最高值,因此表明再次。

没有order by子句,它将始终考虑该ID的所有值,因此它将3视为所有ID的最大值。

有关如何确定该问题的详细信息,请参见the documentation for analytic functions

  

这组行称为窗口,由 analytic_clause 定义。对于每一行,定义了行的滑动窗口。该窗口确定用于执行当前行计算的行范围。窗口大小可以基于物理的行数或逻辑间隔(例如时间)。

  

除非指定了order_by_clause,否则您不能指定[ windowing_clause ]。

  

如果您完全省略 windowing_clause ,则默认值为RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW

答案 1 :(得分:5)

这是正确的,尽管看起来很奇怪。

在MAX上允许的order by子句是一个窗口函数,它允许order函数也包含一个windowing子句-因此,通过指定order by子句,您可以选择windowing子句的默认行为(因为您未指定)。

默认值为RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW

文档:https://docs.oracle.com/database/121/SQLRF/functions004.htm#SQLRF06174

  

如果您完全省略windowing_clause,则默认值为RANGE   在不受限制的前言和当前行之间。