sql - Oracle MIN 作为分析函数 - ORDER BY 的奇怪行为?

标签 sql sql-server oracle oracle10g analytic-functions

这个特殊情况是从一个示例中提炼出来的,其中程序员假设对于两次装运到 jar 车的 cargo ,第 1 行将首先装载。我更正了这一点,以允许按任何顺序执行加载 - 但是,我发现 MIN() OVER (PARTITION BY) 允许在 Oracle 中使用 ORDER BY(这SQL Server 中不允许),此外,它还改变了函数的行为,导致 ORDER BY 显然被添加到 PARTITION BY 中。

WITH data AS (
SELECT 1 AS SHIPMENT_ID, 1 AS LINE_NUMBER, 2 AS TARE, 3 AS GROSS FROM DUAL
UNION ALL
SELECT 1 AS SHIPMENT_ID, 2 AS LINE_NUMBER, 1 AS TARE, 2 AS GROSS FROM DUAL
)
SELECT MIN(tare) OVER (PARTITION BY shipment_id) first_tare
,MAX(gross) OVER (PARTITION BY shipment_id) last_gross
,FIRST_VALUE(tare) OVER (PARTITION BY shipment_id ORDER BY LINE_NUMBER) first_tare_incorrect
,FIRST_VALUE(gross) OVER (PARTITION BY shipment_id ORDER BY LINE_NUMBER DESC) last_gross_incorrect
,MIN(tare) OVER (PARTITION BY shipment_id ORDER BY LINE_NUMBER) first_tare_incorrect_still
,MAX(gross) OVER (PARTITION BY shipment_id ORDER BY LINE_NUMBER DESC) last_gross_incorrect_still
,MIN(tare) OVER (PARTITION BY shipment_id, LINE_NUMBER) first_tare_incorrect_still2
,MAX(gross) OVER (PARTITION BY shipment_id, LINE_NUMBER) last_gross_incorrect_still2
 FROM data

SQL Server 示例(已注释掉不适用的代码):

WITH data AS (
SELECT 1 AS SHIPMENT_ID, 1 AS LINE_NUMBER, 2 AS TARE, 3 AS GROSS -- FROM DUAL
UNION ALL
SELECT 1 AS SHIPMENT_ID, 2 AS LINE_NUMBER, 1 AS TARE, 2 AS GROSS -- FROM DUAL
)
SELECT MIN(tare) OVER (PARTITION BY shipment_id) first_tare
,MAX(gross) OVER (PARTITION BY shipment_id) last_gross
-- ,FIRST_VALUE(tare) OVER (PARTITION BY shipment_id ORDER BY LINE_NUMBER) first_tare_incorrect
-- ,FIRST_VALUE(gross) OVER (PARTITION BY shipment_id ORDER BY LINE_NUMBER DESC) last_gross_incorrect
-- ,MIN(tare) OVER (PARTITION BY shipment_id ORDER BY LINE_NUMBER) first_tare_incorrect_still
-- ,MAX(gross) OVER (PARTITION BY shipment_id ORDER BY LINE_NUMBER DESC) last_gross_incorrect_still
,MIN(tare) OVER (PARTITION BY shipment_id, LINE_NUMBER) first_tare_incorrect_still2
,MAX(gross) OVER (PARTITION BY shipment_id, LINE_NUMBER) last_gross_incorrect_still2
 FROM data

那么问题来了:Oracle 在做什么,为什么这样做,这样做是否正确?

最佳答案

如果将 ORDER BY 添加到 MIN 分析函数,则会将其转换为“到目前为止的最小值”函数,而不是总体最小值。对于您分区所依据的最后一行,结果将是相同的。但前面的行可能具有与总体最小值不同的“到目前为止最小值”。

EMP表为例,您可以看到该部门迄今为止的最低工资最终收敛于该部门的总体最低工资。您可以看到,任何给定部门的“min so far”值都会随着遇到较低值而减小。

SQL> ed
Wrote file afiedt.buf

  1  select ename,
  2         deptno,
  3         sal,
  4         min(sal) over (partition by deptno order by ename) min_so_far,
  5         min(sal) over (partition by deptno) min_overall
  6    from emp
  7*  order by deptno, ename
SQL> /

ENAME          DEPTNO        SAL MIN_SO_FAR MIN_OVERALL
---------- ---------- ---------- ---------- -----------
CLARK              10       2450       2450        1300
KING               10       5000       2450        1300
MILLER             10       1300       1300        1300
ADAMS              20       1110       1110         800
FORD               20       3000       1110         800
JONES              20       2975       1110         800
SCOTT              20       3000       1110         800
smith              20        800        800         800
ALLEN              30       1600       1600         950
BLAKE              30       2850       1600         950
MARTIN             30       1250       1250         950
SM0                30        950        950         950
TURNER             30       1500        950         950
WARD               30       1250        950         950
BAR
PAV

16 rows selected.

当然,当您尝试执行诸如计算个人最佳成绩之类的操作以便在未来期间进行比较时,使用这种形式的分析函数会更有意义。如果您正在跟踪个人高尔夫成绩、英里时间或体重的下降情况,那么展示个人最佳成绩可能是一种激励形式。

SQL> ed
Wrote file afiedt.buf

  1  with golf_scores as
  2  (  select 1 golfer_id, 80 score, sysdate dt from dual union all
  3     select 1, 82, sysdate+1 dt from dual union all
  4     select 1, 72, sysdate+2 dt from dual union all
  5     select 1, 75, sysdate+3 dt from dual union all
  6     select 1, 71, sysdate+4 dt from dual union all
  7     select 2, 74, sysdate from dual )
  8  select golfer_id,
  9         score,
 10         dt,
 11         (case when score=personal_best
 12               then 'New personal best'
 13               else null
 14           end) msg
 15    from (
 16  select golfer_id,
 17         score,
 18         dt,
 19         min(score) over (partition by golfer_id
 20                              order by dt) personal_best
 21    from golf_scores
 22*        )
SQL> /

 GOLFER_ID      SCORE DT        MSG
---------- ---------- --------- -----------------
         1         80 12-SEP-11 New personal best
         1         82 13-SEP-11
         1         72 14-SEP-11 New personal best
         1         75 15-SEP-11
         1         71 16-SEP-11 New personal best
         2         74 12-SEP-11 New personal best

6 rows selected.

关于sql - Oracle MIN 作为分析函数 - ORDER BY 的奇怪行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7393974/

相关文章:

php - 在这种情况下如何获得外部 foreach 循环值?

mysql - SQL/Mysql查询数据库中可用日期

sql-server - SQL Server 为 float 弥补了额外的精度?

sql - 从count大于1的地方删除,但保留一行

sql - 将一组行状态合并为一个摘要状态

sql - 这是 SQL Server 中的错误吗?

选择查询中的 SQL 条件变量

sql - 在 SQL Server 的 select 语句中使用带有 TOP 的变量而不使其动态化

java - Grails 错误的别名导致错误 ORA-00904

sql - Oracle - 对结果集的每一行执行操作