Что не так с этим запросом FIRST_VALUE? - PullRequest
3 голосов
/ 04 августа 2009

Запрос следующий:

with t
as (
  select 450 id, null txt , 3488 id_usr from dual union all
  select 449   , null     , 3488        from dual union all
  select  79   , 'A'      , 3488        from dual union all
  select  78   , 'X'      , 3488        from dual 
)
select id
     , txt
     , id_usr
     , first_value(txt ignore nulls) over (partition by id_usr order by id desc) first_one
  from t

И возвращает:

ID  TXT     D_USR   FIRST_ONE
450         3488    
449         3488    
79  A       3488    A
78  X       3488    A

Это было ожидаемое:

ID  TXT     ID_USR  FIRST_ONE
450         3488    A
449         3488    A
79  A       3488    A
78  X       3488    A

Что не так и почему?

1 Ответ

7 голосов
/ 04 августа 2009

По умолчанию RANGE / ROWS для FIRST_VALUE (как и для любой другой аналитической функции): BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW.

Если добавить IGNORE NULLS, то значения NULL не учитываются при построении диапазона.

RANGE становится BETWEEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCEPT FOR THE NULL ROWS (это недопустимое предложение OVER).

Поскольку ваши txt, которые NULL имеют высокие значения id, они выбираются первыми, а их диапазоны пусты, поскольку между ними и UNBOUNDED PRECEDING нет строк, отличных от NULL.

Вы должны изменить либо ORDER BY, либо RANGE предложение вашего запроса.

Изменение ORDER BY помещает строки с NULL идентификаторами в конец окна, так что сначала будет всегда выбрано не NULL значение (если оно есть), а RANGE гарантированно начнется с значение:

with t
as (
  select 450 id, null txt , 3488 id_usr from dual union all
  select 449   , null     , 3488        from dual union all
  select  79   , 'A'      , 3488        from dual union all
  select  78   , 'X'      , 3488        from dual 
)
select id
     , txt
     , id_usr
     , first_value(txt) over (partition by id_usr order by NVL2(TXT, NULL, id) DESC) first_one
  from t

Изменение RANGE переопределяет диапазон, чтобы включить все не NULL строки в раздел:

with t
as (
  select 450 id, null txt , 3488 id_usr from dual union all
  select 449   , null     , 3488        from dual union all
  select  79   , 'A'      , 3488        from dual union all
  select  78   , 'X'      , 3488        from dual 
)
select id
     , txt
     , id_usr
     , first_value(txt IGNORE NULLS) over (partition by id_usr order by id DESC RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) first_one
  from t
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...