Oracle LAST_VALUE仅在分析子句中使用order by

时间:2019-03-09 12:09:09

标签: sql oracle oracle11g

我有架构(Oracle 11g R2):

CREATE TABLE users (
  id INT NOT NULL,
  name VARCHAR(30) NOT NULL,
  num int NOT NULL
);

INSERT INTO users (id, name, num) VALUES (1,'alan',5);
INSERT INTO users (id, name, num) VALUES (2,'alan',4);
INSERT INTO users (id, name, num) VALUES (3,'julia',10);
INSERT INTO users (id, name, num) VALUES (4,'maros',77);
INSERT INTO users (id, name, num) VALUES (5,'alan',1);
INSERT INTO users (id, name, num) VALUES (6,'maros',14);
INSERT INTO users (id, name, num) VALUES (7,'fero',1);
INSERT INTO users (id, name, num) VALUES (8,'matej',8);
INSERT INTO users (id, name, num) VALUES (9,'maros',55);

然后我执行以下查询-仅将LAST_VALUE分析函数与ORDER BY分析函数一起使用:

我的假设是此查询将在一个分区上执行-整个表(缺少partition by子句)。它将在给定分区(整个表)中按名称对行进行排序,并将使用默认的窗口条款RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW

select us.*, 
last_value(num) over (order by name) as lv 
from users us;

但是上面执行的查询将给出与下一个完全相同的结果。我对第二个查询的假设是,该查询首先按名称对表行进行分区,然后按num对每个分区中的行进行排序,然后在每个分区上应用开窗子句RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING以获得LAST_VALUE

select us.*, 
last_value(num) over (partition by name order by num RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) as lv 
from users us;

我的一个假设显然是错误的,因为上述两个查询给出的结果相同。看起来第一个查询订单记录也以数字的形式出现在幕后。您能否提出我的假设有什么问题以及为什么这些查询返回相同的结果?

enter image description here

3 个答案:

答案 0 :(得分:2)

答案很简单。无论出于何种原因,在windowing子句中使用逻辑LAST_VALUE)偏移量时(默认情况下是显式还是隐式),Oracle选择使RANGE具有确定性。具体来说,在这种情况下,从order by排序所关联的一组行中选择测得表达式的HIGHEST值。

https://docs.oracle.com/en/database/oracle/oracle-database/12.2/sqlrf/LAST_VALUE.html#GUID-A646AF95-C8E9-4A67-87BA-87B11AEE7B79

在Oracle文档的该页面底部,我们可以阅读:

  

如果发现ORDER BY表达式重复,则LAST_VALUE   是 expr [...]

的最大值

为什么文档在示例部分而不是在功能说明中说呢?因为在大多数情况下,该文档似乎不是由合格的人员撰写的。

答案 1 :(得分:1)

Here是db <>小提琴,以防万一有人想和他们玩。

让我假设您认为第二个查询返回的是正确的结果。

select us.*,
       last_value(num) over (partition by name
                             order by num
                             RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
                            ) as lv
from users us;

让我也指出,这更简洁地写为:

select us.*,
       max(num) over (partition by name
                      order by num
                     ) as lv
from users us;

这与您的问题无关,但我想指出。

现在,为什么会给出相同的结果?

select us.*,
       last_value(num) over (order by name) as lv
from users us;

好吧,没有窗口子句,它等效于:

select us.*,
       last_value(num) over (order by name
                             range between unbounded preceding and current row
                            ) as lv
from users us;

range在这里非常重要。它不会转到当前行。它会转到在name中具有相同值的所有行。

据我对order by周围文档的理解,可以从具有相同名称的行中选择 any num值。为什么?在SQL(和Oracle)中排序不稳定。这意味着不能保证保留行的原始顺序。

在这种特殊情况下,最后一个值恰巧是最大值可能是巧合。或者,由于某种原因,Oracle出于某种原因可能会在订单中添加num

答案 2 :(得分:0)

在Oracle杂志的this blog中,如果您在窗口函数中使用ORDER BY子句而不指定其他内容,则会发生以下情况:

  

在没有其他windowing子句参数的情况下,ORDER BY子句有效地添加了一个默认的windowing子句:RANGE UNBOUNDED PRECEDING,这表示“当前分区中的当前行和前一行是应在当ORDER BY子句不附带PARTITION子句时,解析函数使用的整个行集是默认的当前分区。

因此,您的第一个查询实际上与此相同:

SELECT us.*, LAST_VALUE(num) OVER (ORDER BY name RANGE UNBOUNDED PRECEDING) AS lv
FROM users us;

如果运行上面的查询,您将获得所看到的当前行为,这将为每个名称返回一个单独的最后一个值。这与以下查询不同:

SELECT
    us.*,
    LAST_VALUE(num) OVER (ORDER BY name
        RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS lv
FROM users us;

这只会为num的最后一个值生成值8,该值与matej的值相对应,后者是对名称进行升序排序时的姓氏。