row_number() over VS max() over

dbms
Дата: 01.12.2009 16:09:00
Привет.

Для поиска строки с максимальным значением поля Х можно использовать
row_number() over(partition by ... order by X desc) rn
...
where rn = 1 
и
max() over(partition by ... order by X)

Те, кто использовали оба варианта, расскажите, плз, в каких случаях какой из них оказывался быстрее на обрабатываемых Вами данными.
Maxim Demenko
Дата: 01.12.2009 16:38:18
dbms
Привет.

Для поиска строки с максимальным значением поля Х можно использовать
row_number() over(partition by ... order by X desc) rn
...
where rn = 1 
и
max() over(partition by ... order by X)

Те, кто использовали оба варианта, расскажите, плз, в каких случаях какой из них оказывался быстрее на обрабатываемых Вами данными.


1) autotrace вроде бы еще никто не отменял
2) чисто из общих соображений, разница в выполнении двух аналитических функций должна быть пренебрежительно малой
3) как минимум, кроме данных, схема индексирования может сыграть определенную роль
4) есть масса альтернатив нахождения строки с максимальным значением поля, с принципиально другим методом доступа чем у аналитических функций, может имело бы смысл сравнивать с ними?
5) лично я использовал бы для этого агрегатную функцию ( если поле уникально - то first/last)

Best regards

Maxim
andrey_anonymous
Дата: 01.12.2009 17:25:04
dbms
каких случаях какой из них оказывался быстрее

max(...) over(partition by...)
может дать преимущество на больших окнах, поскольку не требует сортировки.
На небольших окнах - без разницы, кмк.
Zloxa
Дата: 01.12.2009 17:54:11
я туплю, или же действительно max() over(partition by ... order by X) нам мало поможет в деле " поиска строки с максимальным значением поля Х"?
suPPLer
Дата: 01.12.2009 18:04:23
Zloxa,

я думаю, что andrey_anonymous, как и ТС, имел в виду следующее:
select *
  from (select t.*, max(...) over(...) mx from t ...)
 where ... = mx

Просто ТС туда зачем-то воткнул ORDER BY. :)
Добрый Э - Эх
Дата: 02.12.2009 05:38:48
Ну и опять же, на НЕУНИКАЛЬНОМ поле Х результаты запросов будут разными - с ROW_NUMBER() гарантированно вернется одна строка по каждой секции. С MAX() же - столько строк на каждую секцию, сколько найдется одинаковых максимумом по полю Х:
--
-- Тестовые данные:
with
  t as
    (
      select 1 as part_id, 1 as x, 'bla-bla-bla-11' as any_column from dual union all
      select 1 as part_id, 2 as x, 'bla-bla-bla-12' as any_column from dual union all
      select 1 as part_id, 3 as x, 'bla-bla-bla-13' as any_column from dual union all
      select 2 as part_id, 1 as x, 'bla-bla-bla-21' as any_column from dual union all
      select 2 as part_id, 2 as x, 'bla-bla-bla-22' as any_column from dual union all
      select 2 as part_id, 3 as x, 'bla-bla-bla-23' as any_column from dual union all
      select 2 as part_id, 3 as x, 'bla-bla-bla-24' as any_column from dual union all
      select 3 as part_id, 1 as x, 'bla-bla-bla-31' as any_column from dual union all
      select 3 as part_id, 1 as x, 'bla-bla-bla-32' as any_column from dual union all
      select 3 as part_id, 1 as x, 'bla-bla-bla-33' as any_column from dual
    )
--
-- Основной запрос:
select *
  from (
         select t.*, 
                row_number() 
                      over(partition by part_id 
                               order by x desc) as rn
           from t
       )
 where rn = 1

Query finished, retrieving results...

PART_ID    X      ANY_COLUMN     RN
-------   ---   --------------   --
      1     3   bla-bla-bla-13    1
      2     3   bla-bla-bla-23    1
      3     1   bla-bla-bla-31    1

3 row(s) retrieved




--
-- Тестовые данные:
 with
  t as
    (
      select 1 as part_id, 1 as x, 'bla-bla-bla-11' as any_column from dual union all
      select 1 as part_id, 2 as x, 'bla-bla-bla-12' as any_column from dual union all
      select 1 as part_id, 3 as x, 'bla-bla-bla-13' as any_column from dual union all
      select 2 as part_id, 1 as x, 'bla-bla-bla-21' as any_column from dual union all
      select 2 as part_id, 2 as x, 'bla-bla-bla-22' as any_column from dual union all
      select 2 as part_id, 3 as x, 'bla-bla-bla-23' as any_column from dual union all
      select 2 as part_id, 3 as x, 'bla-bla-bla-24' as any_column from dual union all
      select 3 as part_id, 1 as x, 'bla-bla-bla-31' as any_column from dual union all
      select 3 as part_id, 1 as x, 'bla-bla-bla-32' as any_column from dual union all
      select 3 as part_id, 1 as x, 'bla-bla-bla-33' as any_column from dual
    )
--
-- Основной запрос:
select *
  from (
         select t.*, 
                max(x) over(partition by part_id 
                                order by x desc) as max_x
           from t
       )
 where x = max_x

Query finished, retrieving results...

PART_ID    X      ANY_COLUMN     MAX_X
-------   ---   --------------   -----
      1     3   bla-bla-bla-13       3
      2     3   bla-bla-bla-24       3
      2     3   bla-bla-bla-23       3
      3     1   bla-bla-bla-31       1
      3     1   bla-bla-bla-32       1
      3     1   bla-bla-bla-33       1

6 row(s) retrieved
wurdu
Дата: 02.12.2009 07:36:49
andrey_anonymous
может дать преимущество на больших окнах, поскольку не требует сортировки.
На небольших окнах - без разницы, кмк.
Сортировка в любом случае нужна, см. план. Собственно эта сортировка и убивает производительность аналитики на боле-менее серьезных объемах данных.
С аналитикой:
create table tst nologging as select mod(rownum, 100) rn, rownum rn2 from dual connect by level <= 5000000;

Table created.

Elapsed: 00:00:25.04
select rn, rn2 from(
  2  select a.*, max(rn2) over(partition by rn ) maxrn from tst a
  3  ) where rn2 = maxrn;

100 rows selected.

Elapsed: 00:00:25.84

Execution Plan
----------------------------------------------------------
Plan hash value: 1888975516

------------------------------------------------------------------------------------
| Id  | Operation           | Name | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      |  3869K|   143M|       | 26397   (3)| 00:03:54 |
|*  1 |  VIEW               |      |  3869K|   143M|       | 26397   (3)| 00:03:54 |
|   2 |   WINDOW SORT       |      |  3869K|    95M|   267M| 26397   (3)| 00:03:54 |
|   3 |    TABLE ACCESS FULL| TST  |  3869K|    95M|       |   754  (13)| 00:00:07 |
------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("RN2"="MAXRN")


Statistics
----------------------------------------------------------
        147  recursive calls
         11  db block gets
       9593  consistent gets
      43100  physical reads
          0  redo size
       1552  bytes sent via SQL*Net to client
        285  bytes received via SQL*Net from client
          8  SQL*Net roundtrips to/from client
          0  sorts (memory)
          1  sorts (disk)
        100  rows processed
Без аналитики
select a.rn, a.rn2 from tst a,(
  2  select rn, max(rn2) maxrn from  tst a 
  3  group by rn) b where a.rn = b.rn and rn2 = maxrn;

100 rows selected.

Elapsed: 00:00:06.85

Execution Plan
----------------------------------------------------------
Plan hash value: 270462357

-------------------------------------------------------------------------------------
| Id  | Operation            | Name | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |      |    19M|   954M|       |  9001  (12)| 00:01:20 |
|*  1 |  HASH JOIN           |      |    19M|   954M|   140M|  9001  (12)| 00:01:20 |
|   2 |   TABLE ACCESS FULL  | TST  |  3869K|    95M|       |   754  (13)| 00:00:07 |
|   3 |   VIEW               |      |  3869K|    95M|       |  1243  (47)| 00:00:12 |
|   4 |    HASH GROUP BY     |      |  3869K|    95M|       |  1243  (47)| 00:00:12 |
|   5 |     TABLE ACCESS FULL| TST  |  3869K|    95M|       |   754  (13)| 00:00:07 |
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("A"."RN"="B"."RN" AND "RN2"="MAXRN")

Statistics
----------------------------------------------------------
        116  recursive calls
          0  db block gets
      19380  consistent gets
      11904  physical reads
          0  redo size
       1617  bytes sent via SQL*Net to client
        285  bytes received via SQL*Net from client
          8  SQL*Net roundtrips to/from client
          4  sorts (memory)
          0  sorts (disk)
        100  rows processed
ClonCD
Дата: 02.12.2009 15:26:40
wurdu, отлично!

Я тут тоже набросал запросик.
Вот такая аналитика работает ~ на 10% быстрее на представленных данных, чем твой запрос без аналитики:

select a.rn
  , max(a.rn2) keep (dense_rank last order by a.rn2) rn
from tst a
group by a.rn

План такой:

Rows     Row Source Operation
-------  ---------------------------------------------------
    100  SORT GROUP BY (cr=9535 pr=9531 pw=9531 time=1 us cost=2918 size=142753832 card=5490532)
5000000   TABLE ACCESS FULL TST (cr=9535 pr=9531 pw=9531 time=10193 us cost=2667 size=142753832 card=5490532)
dbms_photoshop
Дата: 02.12.2009 16:09:14
ClonCD
wurdu, отлично!

Я тут тоже набросал запросик.
Вот такая аналитика работает ~ на 10% быстрее на представленных данных, чем твой запрос без аналитики:

\nselect a.rn\n  , max(a.rn2) keep (dense_rank last order by a.rn2) rn\nfrom tst a\ngroup by a.rn\n

Прочитай про агрегатные функции.

2ТС:
Строка с минимальным значением поля
Добрый Э - Эх
Дата: 03.12.2009 05:32:39
ClonCD
...
Вот такая аналитика ...
...
...
  , max(a.rn2) keep (dense_rank last order by a.rn2) rn
...

Друже, ты такой же темный и дремучий, как еловый лес за моим домом. Тут аналитики и на грамм нету. А то что ты показал, как ни странно, относится к агрегатным функциям, но не к аналитическим...