Oracle LAST_VALUE только с заказом по в аналитическом предложении - PullRequest
2 голосов
/ 09 марта 2019

У меня есть схема (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 аналитическим предложением:

Я предполагаю, чточто этот запрос выполняется по одному разделу - целой таблице (поскольку раздел по разделам отсутствует).Он будет сортировать строки по имени в данном разделе (целой таблице) и будет использовать оконное предложение по умолчанию 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;

Oneмоего предположения явно неверно, потому что два вышеупомянутых запроса дают одинаковый результат.Похоже, что первые записи заказов запросов также по Num за занавесками.Не могли бы вы подсказать, что не так с моими предположениями и почему эти запросы дают одинаковые результаты?

enter image description here

Ответы [ 3 ]

2 голосов
/ 10 марта 2019

Ответ прост. По какой-либо причине Oracle решила сделать LAST_VALUE детерминированным, когда в предложении windowing (по умолчанию или неявно - по умолчанию) используется смещение логическое (RANGE). В частности, в таких случаях ВЫСОКОЕ значение измеренного выражения выбирается из набора строк, связанных сортировкой order by.

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 [...]

Почему в документации говорится, что в разделе examples , а не в объяснении функции? Потому что, как это часто бывает, документация не написана квалифицированными специалистами.

1 голос
/ 09 марта 2019

Здесь - это дб <> скрипка, на случай, если кто-то захочет поиграть с ними.

Позвольте мне предположить, что вы думаете, что второй запрос возвращает правильные результаты.

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, может быть выбрано любое значение num из строк с одинаковым именем. Зачем? Сортировка в SQL (и в Oracle) нестабильна. Это означает, что не гарантируется сохранение исходного порядка строк.

В этом конкретном случае может быть совпадением то, что последнее значение оказывается наибольшим. Или, по какой-то причине, Oracle может по какой-то причине добавить num к заказу.

0 голосов
/ 09 марта 2019

Из этого блога в журнале Oracle, вот что происходит, если вы используете предложение ORDER BY в оконной функции, не указывая ничего другого:

Предложение ORDER BY при отсутствии каких-либо дополнительных параметров предложения оконного управления фактически добавляет стандартное оконное предложение: 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;

Это просто генерирует значение 8 для последнего значения num, которое соответствует значению для matej, который является фамилией при сортировке имени по возрастанию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...