Производительность: rank () против подзапроса.Подзапрос имеет более низкую стоимость? - PullRequest
1 голос
/ 10 февраля 2012

Вдохновленный этим вопросом Я решил протестировать функцию rank(), пытаясь определить, являются ли подзапросы менее эффективными, чем рейтинг. Итак, я создал таблицу:

create table teste_rank ( codigo number(7), data_mov date, valor number(14,2) );
alter table teste_rank add constraint tst_rnk_pk primary key ( codigo, data_mov );

и вставил несколько записей ...

declare
  vdata date;
begin
  dbms_random.initialize(120401);
  vdata := to_date('04011997','DDMMYYYY');
  for reg in 1 .. 465 loop
    vdata := to_date('04011997','DDMMYYYY');
    while vdata <= trunc(sysdate) loop
      insert into teste_rank 
          (codigo, data_mov, valor) 
        values 
          (reg, vdata, dbms_random.value(1,150000));
      vdata := vdata + 2;
    end loop;
    commit;
  end loop;
end;
/

А затем проверил два запроса:

select * 
  from teste_rank r
 where r.data_mov = ( select max(data_mov) 
                        from teste_rank 
                       where data_mov <= trunc(sysdate) 
                         and codigo = 1 )
   and r.codigo = 1;

Explain Plan for sub query


select *
  from ( select rank() over ( partition by codigo order by data_mov desc ) rn, t.*
           from teste_rank t
          where codigo = 1
            and data_mov <= trunc(sysdate) ) r
 where r.rn = 1;

Explain plan rank()

Как видите, стоимость подзапроса ниже, чем rank (). Это правильно? Я что-то там упускаю?

PS: протестировано также с полным запросом в таблице и по-прежнему подзапросом с низкой стоимостью.

EDIT

Я сгенерировал tkprof из двух запросов (отслеживаемый, закрытие базы данных, запуск и отслеживание второго).

Для подзапроса

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.02          3          5          0           0
Execute      1      0.00       0.00          0          3          0           0
Fetch        2      0.00       0.00          1          4          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.00       0.02          4         12          0           1

Для rank()

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.02          3          3          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.00       0.00          9         19          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.01       0.03         12         22          0           1

Можно ли сделать вывод, что подзапрос не всегда будет менее эффективным, чем рейтинг? Когда указывается ранг вместо подзапроса?

Ответы [ 2 ]

2 голосов
/ 10 февраля 2012

Я не совсем уверен, каков твой вопрос. Да, согласно этим двум планам выполнения, в этом случае метод подзапроса имеет более низкую ожидаемую стоимость. В этом нет ничего удивительного, так как он может использовать индекс, чтобы очень быстро найти нужную строку. В данном случае подзапрос должен выполнить очень быструю проверку индекса PK. Ситуация может отличаться, если подзапрос включает столбцы, которые не являются частью индекса.

Запрос, использующий rank(), должен получить все подходящие строки и ранжировать их. Я не верю, что оптимизатор обладает какой-либо логикой короткого замыкания, чтобы распознать, что это запрос top-n, и поэтому избегать полной сортировки, даже если все, что вас действительно волнует, это строка верхнего рейтинга.

Вы также можете попробовать эту форму, которую оптимизатор должен распознавать как запрос top-n. В вашем случае я ожидаю, что для этого потребуется только одно сканирование диапазона индекса, а затем доступ к таблице.

select * 
  from (select *
          from teste_rank r
          where data_mov <= trunc(sysdate) 
            and codigo = 1
        order by data_mov desc)
  where rownum=1;
2 голосов
/ 10 февраля 2012

Стоимость - это оценка оптимизатора на основе затрат того, что потребуется для выполнения запроса.

Возможно, что CBO может ошибиться, особенно если статистика устарела.

Так что делать?Попробуйте выполнить каждый запрос с помощью 'set autotrace on'.Сколько буфера получает и физическое чтение выполняет каждый запрос?Другими словами, сколько фактических трудов выполняет каждый запрос?

Надеюсь, это поможет.

...