Re[14]: определить позицию строки в запросе
От: Овощ http://www.google.com
Дата: 02.09.09 07:52
Оценка: 98 (2)
Здравствуйте, MasterZiv, Вы писали:

MZ>Ээээ.. доказательства будут ?


MZ> Оракл понимает, что такое rownum и как тут можно

>> оптимизировать.

MZ>Как можно что-то оптимизировать по rownum, если он генерируется

MZ>в момент создания набора данных ?
MZ>Объясните, я не очень понимаю.

Попробую объяснить как все это происходит в оракле.
Рассмотрим два случая: с наличием индекса для сортировки, и без оного.

Для первного случая создаем простую таблицу:
SQL> create table t1 (i integer primary key);

Для первичного ключа будет создан нужный нам индекс.
Заполняем эту таблицу числами от 1 до 10000000.
SQL> insert into t1 select rownum from dual connect by level <= 10000000;
10000000 rows created.

Собираем статистику по таблице — для оптимизатора:
exec dbms_stats.gather_table_stats(ownname=>user, cascade=>true, tabname=>'t1');

Теперь собственно сам запрос. Для начала рассмотрим только запрос аналогичный конструкции TOP N, т.е. получение первых N записей в порядке сортировки.
Выглядить он будет так (N=100):
select t.i
from
(
    select * from t1 order by i
) t
where rownum <= 100

с планом:
----------------------------------------------------------------------------------
| Id  | Operation         | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |              |   100 |  1300 |     3   (0)| 00:00:01 |
|*  1 |  COUNT STOPKEY    |              |       |       |            |          |
|   2 |   VIEW            |              |   100 |  1300 |     3   (0)| 00:00:01 |
|   3 |    INDEX FULL SCAN| SYS_C0088663 |  9875K|    47M|     3   (0)| 00:00:01 |
----------------------------------------------------------------------------------

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

   1 - filter(ROWNUM<=100)


План означает следующее. Сортировка была заменена на упорядоченное чтение индекса (INDEX FULL SCAN). Более того строки, последовательно читаемые (в порядке сортировки) из индекса передаются на вход операции COUNT STOPKEY (filter(ROWNUM<=100). Эта операция фактически считает кол-во строк, которые прошли через нее и когда достигается заранее заданное значение (в нашем случае 100), то дальнейшее выполнение запроса останавливается.
Т.е. весь запрос хоть и обращается к таблице с 10000000 записей, но читает лишь первые несколько блоков из индекса и останавливается, когда прочитает ровно то кол-во строк, которые нужно. Но отсюда сразу виден недостаток этого подхода — в нашем случае у нас есть преимущество в том, что мы задали для чтения лишь 100 строк. Если бы нам потребовался скажем миллион строк, то прочитать нужно намного больше, но опять же не всю таблицу/индекс. Но и в реальной жизни это тоже имеет некоторый смысл — в большинстве случаев любой большой список для пользователя будет показываться в виде небольшой части расположенной с начала этого списка, и лишь некоторые из пользователей будут долго и упорно переходить по страницам, чтобы увидеть конец списка.

Превращение вышеприведенного запроса в полноценный запрос с пейджингом ничего интересного не привнесет (выберем скажем страницы по 10 записей и отобразим 9-ую страницу — записи с 91-ой по 100-ую):

select t.* from
(
    select t.i, rownum as rn
    from
    (
        select * from t1 order by i
    ) t
    where rownum <= 100
) t
where t.rn > 90

-----------------------------------------------------------------------------------
| Id  | Operation          | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |              |   100 |  2600 |     3   (0)| 00:00:01 |
|*  1 |  VIEW              |              |   100 |  2600 |     3   (0)| 00:00:01 |
|*  2 |   COUNT STOPKEY    |              |       |       |            |          |
|   3 |    VIEW            |              |   100 |  1300 |     3   (0)| 00:00:01 |
|   4 |     INDEX FULL SCAN| SYS_C0088663 |  9875K|    47M|     3   (0)| 00:00:01 |
-----------------------------------------------------------------------------------

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

   1 - filter("T"."RN">90)
   2 - filter(ROWNUM<=100)


Фактически запрос выполняется абсолютно точно также как и первый — берется первые 100 записей по индексу, но затем эти 100 записей передаются на вход операции VIEW (filter("T"."RN">90)), которая выполняет самую простую фильтрацию по предикату "RN">90 — для того чтобы не возвращать не нужные записи с номерами меньше 90. Никаких преимуществ (например по чтению с диска) по сравнению с первым запросом здесь нет, разве что кроме того, что на клиент отправляется меньшее количество записей (10 вместо 100). Недостатки такие же как и в первом случае.

Теперь можно рассмотреть случай без использования индекса.
SQL> create table t2 (i integer);
Table created.
SQL> insert into t2 select rownum from dual connect by level <= 10000000;
10000000 rows created.


Запрос с первыми 100 записей (TOP 100).

select t.i
from
(
    select * from t2 order by i
) t
where rownum <= 100

----------------------------------------------------------------------------------------
| Id  | Operation               | Name | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |      |   100 |  1300 |       | 53658   (5)| 00:10:44 |
|*  1 |  COUNT STOPKEY          |      |       |       |       |            |          |
|   2 |   VIEW                  |      |    10M|   126M|       | 53658   (5)| 00:10:44 |
|*  3 |    SORT ORDER BY STOPKEY|      |    10M|   126M|   390M| 53658   (5)| 00:10:44 |
|   4 |     TABLE ACCESS FULL   | T2   |    10M|   126M|       |  3607   (8)| 00:00:44 |
----------------------------------------------------------------------------------------

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

   1 - filter(ROWNUM<=100)
   3 - filter(ROWNUM<=100)


Здесь уже индекса нет, поэтому приходится читать всю таблицу — TABLE ACCESS FULL T2.
Однако полной сортировки не происходит, ввиду того что Oracle опять распознал использование rownum и понял что ему нужно лишь определенное фиксированное количество записей (100). Происходит это в операции SORT ORDER BY STOPKEY (filter(ROWNUM<=100)). Эта операция выделяет лишь небольшой буфер в памяти (у нас — на 100 элементов) и организует в нем что-то типа очереди с приоритетами. По мере того как будут читаться записи из таблицы, каждая запись будет сравниваться с теми записями, которые уже находятся в буфере, и оцениваться на то, попадает ли она в этот буфер (возможно вытесняя из него другие записи, помещенные в него раньше) или же просто отбрасывает новые строки. В любом случае на выходе этой операции будет ровно 100 нужных нам элементов отсортированных в нужном нам порядке. Это видно по трассировке:

Rows     Row Source Operation
-------  ---------------------------------------------------
    100  COUNT STOPKEY (cr=15208 pr=0 pw=0 time=1992552 us)
    100   VIEW  (cr=15208 pr=0 pw=0 time=1992547 us)
    100    SORT ORDER BY STOPKEY (cr=15208 pr=0 pw=0 time=1992541 us)
10000000     TABLE ACCESS FULL T2 (cr=15208 pr=0 pw=0 time=29 us)

Операция COUNT STOPKEY в данном случае не особо то и нужна, поскольку лишь дублирует функции SORT ORDER BY STOPKEY.
При относительно небольших N такая операция будет значительно менее затратной по ресурсам чем полная сортировка таблицы.

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

select t.* from
(
    select t.i, rownum as rn
    from
    (
        select * from t2 order by i
    ) t
    where rownum <= 100
) t
where t.rn > 90

-----------------------------------------------------------------------------------------
| Id  | Operation                | Name | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT         |      |   100 |  2600 |       | 53658   (5)| 00:10:44 |
|*  1 |  VIEW                    |      |   100 |  2600 |       | 53658   (5)| 00:10:44 |
|*  2 |   COUNT STOPKEY          |      |       |       |       |            |          |
|   3 |    VIEW                  |      |    10M|   126M|       | 53658   (5)| 00:10:44 |
|*  4 |     SORT ORDER BY STOPKEY|      |    10M|   126M|   390M| 53658   (5)| 00:10:44 |
|   5 |      TABLE ACCESS FULL   | T2   |    10M|   126M|       |  3607   (8)| 00:00:44 |
-----------------------------------------------------------------------------------------

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

   1 - filter("T"."RN">90)
   2 - filter(ROWNUM<=100)
   4 - filter(ROWNUM<=100)


Некоторые выводы:
1) Для оптимизации необходимы индексы. Поскольку наибольшая скорость будет именно с использованием индексов вместо сортировок.
2) Если нам нужно получить записи с номерами от :start до :end, то критичным является именно номер последней строки (:end), поскольку запрос в общем случае "выполняется" для всех записей с номерами от 1 до :end. Т.е. другими словами, это будет эффективно лишь если мы показываем страницы записей с небольшими номерами. Чем дальше отображаемая страница находится к концу списка страниц тем медленне будет запрос.

Вот как то так...
Re[12]: определить позицию строки в запросе
От: KRA Украина  
Дата: 01.09.09 14:38
Оценка: +1
Здравствуйте, MasterZiv, Вы писали:

MZ>Фетчить наборы данных можно только целиком. Вообще, если вы выбрали

MZ>набор, потом тот же запросы выполнили, и выбрали набор ещё раз --
MZ>это уже другой набор, к первому не имеющий никакого отношения.

Может я чего не понимаю, но чем плох вариант: сначала определяем на какую страницу попадает выбраная пользователем строка (по сути то с чего началась тема — нужно определить порядковый номер записи с даным идентификатором). Запрос получается вида (пишу с ораклиным синтаксисом)


select rn from  (
select e.*, rownum rn
from table e
order by
  e.field1, e.field2
) 
where id = :id


при наличии индексов по полям сортировки даже при 100к может работать с приемлимой скоростью на соответствующем железе (по крайней мере оракл с rownum-ами неплохо работает).

После этого, зная номер записи (и расчитав на какой странице она находится), мы можем подчитать только нужную нам страницу запросом вида

select e.* from (
select e.*, rownum rn
from table e
order by
  e.field1, e.field2
)
where rn between :start_index and :end_index


Опять же в оракле такой запрос не приводит к тому, что читаются все данные целиком. Оракл понимает, что такое rownum и как тут можно оптимизировать.

Естественно оба запроса в одной транзакции.

MZ>Фетчить большие наборы тоже плохо -- они никому не нужны, даже отсортированные.

MZ>Вам следует не заниматься поисками эфимерности, а улучшить
MZ>критерии выборки, чтобы сократить кол-во выбераемых с сервера
MZ>данных.

В теории согласен. Но вот на практике как это реализовать непонятно. Делать предварительную выборку количества без учёта сортировок и если в результате получается много — говорить пользователю, чтоб задал более жёсткие ограничения? Обычно пользователи хотят задавать любые критерии и иметь пейджинг.
Re[15]: определить позицию строки в запросе
От: Овощ http://www.google.com
Дата: 02.09.09 10:01
Оценка: +1
Здравствуйте, MasterZiv, Вы писали:

Только я не понял, что именно вы предлагаете взамен?
Увидел только это:

MZ>Да, например. Можно по любому запросу выводить, скажем, только первые 100

MZ>записей, и если есть ещё, писать, что часть данных были отброшены.

MZ>Больше 100-1000 записей никто читать не будет. Если это не выгрузка

MZ>данных куда-то для экспорта, или не отчёт какой-то для складирования, бесполезно
MZ>выводить более 100-1000 записей. Конкретные 100-1000 конечно варьируются в
MZ>зависимости от специфики приложения, но в общем идея такая.

MZ>>Обычно пользователи хотят задавать любые критерии и

MZ>>иметь пейджинг.

MZ>Нахрена пейджинг нужен, если ты получаешь 10-100 нужных тебе записей ?


Если у нас есть "первые"/"последние" то видимо есть какой-то порядок (который наверняка задается через order by).
Если нам нужны только 100 записей, то видимо у нас есть ограничение на выборку в виде TOP/rownum.

Получается, что для этого нам надо использовать точно такую же конструкцию из rownum + order by (или top + order by), которая вам не понравилась.
И в конечно счете все получается абсолютно точно также, как и в случаях, которые я расписал выше.
Разницы между пейджинг/непейджинг здесь нет никакой, разве что в микродеталях реализации (зададим жестко, что номер последней строки для выборки не должен быть больше 100 и все — нет пейджинга, а есть предложенный вами вариант).
Не вижу разницы. Она здесь есть?

Путь для "оптимизации" я вижу только один — выбирать не первые 100, а случайные 100. Тогда можно отказаться от сортировки.
Но обычно сортировка результатов поиска просто обязательна в любом приложении (да и само поле сортировки может меняться).

Варинт же с отображением пользователю вместо результатов выборки сообщения вида "Найдено 100000 записей удовлетворяющих критериям поиска. Это слишком много для отображения. Уточните критерии поиска." вообще мне кажется неприемлемым.
Re[14]: определить позицию строки в запросе
От: KRA Украина  
Дата: 02.09.09 10:06
Оценка: +1
Здравствуйте, Овощ, Вы писали:

О>Правильно уже сказали — тут фактическая ошибка. Oracle высчитывает rownum до выполнения order by. Т.е. в общем случае надо делать так (выносить rownum на уровень выше чем order by):


Спасибо за уточнения. Я привёл приблизительный вид запросов, т.к. это не главная мысль. Главная мысль такая, что оптимизатор oracle отлично понимает семантику rownum.
определить позицию строки в запросе
От: ov  
Дата: 28.08.09 13:17
Оценка:
вопрос к тем, кто показывает много данных в таблицах
есть у меня запрос
SELECT Id, ....
FROM ... WHERE .... ORDER BY ...

задача — узнать каким номером будет идти строка с Id=N

по специфике приложения, должна использоваться встроенная БД, я смотрел SQLite, FireBird, MS SQL CE — нигде этого сделать нельзя кроме как полным "выкачиванием" результатов и ручным поиском.

в полноценной версии MS SQL SERVER 2005 вроде есть ROW_NUMBER(), при помощи которой это делается.

а как быть с более простыми базами, есть идеи?

зачем все это надо: чтобы в гриде с сортировкой и фильтрацией восстановить выделенную строчку после смены сортировки. так как записей может быть много, то чтение идет кусками и хотелось бы сразу прочитать нужный кусок.
Re: определить позицию строки в запросе
От: KRA Украина  
Дата: 28.08.09 13:34
Оценка:
Здравствуйте, ov, Вы писали:

ov>а как быть с более простыми базами, есть идеи?



Есть идея, как можно в firebird попытаться проэмулировать rownum. Можно попытаться использовать для этого последовательности, приблизительно так

create sequence temp_seq;
select gen_id(temp_seq,1) rownum from ....

Тут правда возникают другие проблемы, которые нужно ещё подумать как решить
1. недопущение использования одной последовательности в паралельных запросах
2. превышение максимального значения в последовательности (я не уверен, как оно устроено в firebird. вероятно, начнётся опять с нуля(?))
Re[2]: определить позицию строки в запросе
От: DarkMaster Украина http://www.bdslib.at.ua
Дата: 28.08.09 15:42
Оценка:
Здравствуйте, KRA, Вы писали:

ov>>а как быть с более простыми базами, есть идеи?


KRA>Есть идея, как можно в firebird попытаться проэмулировать rownum. Можно попытаться использовать для этого последовательности, приблизительно так


Поправки для FB.

KRA>create sequence temp_seq;



CREATE GENERATOR TEMP_GEN;
SET GENERATOR TEMP_GEN TO 0;
COMMIT;


KRA>select gen_id(temp_seq,1) rownum from ....


Угу.

KRA>Тут правда возникают другие проблемы, которые нужно ещё подумать как решить

KRA>1. недопущение использования одной последовательности в паралельных запросах

Генераторы в FB работают вне контекста транзакций, поэтому при параллельной работе придется делать по генератору на каждый запрос. Потом — удалять. Т.к. пользователь будет владельцем обьекта — все должно пройти нормально (не будет накладок с правами).

KRA>2. превышение максимального значения в последовательности (я не уверен, как оно устроено в firebird. вероятно, начнётся опять с нуля(?))


В FB 2.x генераторы — INT64. Должно хватить по уши. Насчет установки начального значения — смотри выше.

P.S. Более универсальное решение для FB — ххранимая процедура:

  I=0;
  for SELECT ... FROM ... WHERE ...
      INTO ....
  do BEGIN
       I=I+1;
       SUSPEND;
     END
WBR, Dmitry Beloshistov AKA [-=BDS=-]
Re[3]: определить позицию строки в запросе
От: DarkMaster Украина http://www.bdslib.at.ua
Дата: 28.08.09 15:45
Оценка:
Здравствуйте, DarkMaster, Вы писали:

DM>P.S. Более универсальное решение для FB — ххранимая процедура:


Забыл еще про EXECUTE BLOCK;

execute block
returns (AROWNUM INT64, AVALUE varchar(10))
as
begin
  AROWNUM=0; 
  for select VALUE 
      from MYTABLE
      where VALUE LIKE 'A%'
      order by VALUEDATE
      into :AVALUE
  do begin
       AROWNUM=AROWNUM+1;
       suspend;
     end
end
WBR, Dmitry Beloshistov AKA [-=BDS=-]
Re: определить позицию строки в запросе
От: MasterZiv СССР  
Дата: 29.08.09 06:47
Оценка:
ov пишет:



> зачем все это надо: чтобы в гриде с сортировкой и фильтрацией

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


Какая разница, какой по порядку идёт нужная вам строка ?
У каждой строки есть первичный ключ (должен быть, иначе
нужно делать редизайн БД), вы до смены сортировки запоминаете значение
ключа, делаете сортировку, и находите нужную запись по запомненному
значению ключа.
Posted via RSDN NNTP Server 2.1 beta
Re: определить позицию строки в запросе
От: MasterZiv СССР  
Дата: 29.08.09 06:48
Оценка:
ov пишет:

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

А это сделать не получится.
Posted via RSDN NNTP Server 2.1 beta
Re[2]: определить позицию строки в запросе
От: ov  
Дата: 29.08.09 08:50
Оценка:
>вы до смены сортировки запоминаете значение ключа, делаете сортировку, и находите нужную запись по запомненному
>значению ключа.
все бы хорошо, да надо читать блоками. для чтения целиком проблем нет — все так и делается и прекрасно работает.

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

MZ>А это сделать не получится.
прочитать кусок не проблема. SQLite и FireBird позволяют в SELECT'е указать "окно". MS SQL CE, правда, не позволяет, но его старший брат может.
по моим тестам чтение 20К записей (на SQLite) не тормозит и можно читать целиком, но если записей будет 100К?

я отдаю себе отчет, что негоже юзеру показывать 100К записей, но в этом суть программы: длинный список с сортировкой и фильтрацией.
Re[3]: определить позицию строки в запросе
От: MasterZiv СССР  
Дата: 29.08.09 16:22
Оценка:
ov пишет:

> прочитать кусок не проблема. SQLite и FireBird позволяют в SELECT'е

> указать "окно". MS SQL CE, правда, не позволяет, но его старший брат может.
> по моим тестам чтение 20К записей (на SQLite) не тормозит и можно читать
> целиком, но если записей будет 100К?

Какими такими блоками ?
Если речь о всяких там LIMIT-ах или TOP-ах,
то их не имеет смысла применять, если вам нужны не
первые N записей.
Posted via RSDN NNTP Server 2.1 beta
Re[4]: определить позицию строки в запросе
От: ov  
Дата: 30.08.09 04:38
Оценка:
MZ>Если речь о всяких там LIMIT-ах или TOP-ах,
да, о них.
MZ>то их не имеет смысла применять, если вам нужны не первые N записей.
почему? в SQLite есть конструкция LIMIT 100 OFFSET 1000, которая вернет 100 строк с 1000-й позиции. в FireBird есть FIRST/SKIP, кажется.

и как тогда "правильно" считать результат запроса в 100К строк? за раз чтоли??
Re[5]: определить позицию строки в запросе
От: MasterZiv СССР  
Дата: 31.08.09 06:55
Оценка:
ov пишет:

> MZ>Если речь о всяких там LIMIT-ах или TOP-ах,

> да, о них.
> MZ>то их не имеет смысла применять, если вам нужны не первые N записей.
> почему? в SQLite есть конструкция LIMIT 100 OFFSET 1000, которая вернет
> 100 строк с 1000-й позиции. в FireBird есть FIRST/SKIP, кажется.

Потому что LIMIT-ы или TOP-ы в общем случая обрабатывают
сначала ВЕСЬ набор данных, а потом возвращают только его часть.
Экономия -- только на операции FETCH на клиента.

При этом экономии можно достичь только :

0) выбирая только N записей от начала набора данных,
при этом оптимизатор может понять, что от запроса нужно только
первые N строк, и построит план так, чтобы только они обрабатывались,
и то при этом нужно, чтобы операция ORDER BY оптимизировалась за счёт индекса,
а не производилась реальная сортировка, иначе опять-таки будет обрабатываться
весь потенциальный набор данных

1) выбирая N записей начиная с M-ой СУБД придётся обработать подобным же
образом M+N записей, на M записей получится экономия только на FETCH-е на
клиента, и на ещё (размер таблицы — (M+N)) -- может быть экономия, как
в случае (0)


> и как тогда "правильно" считать результат запроса в 100К строк? за раз

> чтоли??

Нужно не питать иллюзий, что есть какой-то магический способ выборки части
набора данных из всего набора. Набор нужно обрабатывать целиком.
Единственный реальный способ оптимизации выборки -- это отбросить
невыбранную часть данных, он работает (почти) всегда и везде.
Но даже в этом случае СУБД может обрабатывать весь большой невыбираемый
набор данных.

Да, вы тут говорили о SQL Lite, возможно, есть какие-то специфичные
для него аспекты этой проблемы, я его к сож. не знаю.
Posted via RSDN NNTP Server 2.1 beta
Re[6]: определить позицию строки в запросе
От: ov  
Дата: 31.08.09 09:35
Оценка:
MZ>Нужно не питать иллюзий, что есть какой-то магический способ выборки части
MZ>набора данных из всего набора. Набор нужно обрабатывать целиком.
да не в иллюзиях дело. просто хочется решить вопрос на уровне БД, оптимальным для нее способом. чтобы я мог ей сказать "хочу такие-то записи, с такой-то позиции", а она уже сама думала как их мне наиболее оптимально выдать.

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

MZ>Да, вы тут говорили о SQL Lite, возможно, есть какие-то специфичные

MZ>для него аспекты этой проблемы, я его к сож. не знаю.
да нет, там своих тараканов хватает. он окно делает как раз пропуская лишние строчки, в результате окно, близкое к началу выборки, летает, а если начинать с полумиллионной строчки — тормозит. MS SQL Server с хитрой конструкцией из ROW_NUMBER'ов летает и на таких количествах. файрберд тупил и на гораздо меньших цифрах, но там, возможно, я с индеками перемудрил.
Re[7]: определить позицию строки в запросе
От: MasterZiv СССР  
Дата: 31.08.09 11:18
Оценка:
ov wrote:

MS SQL Server с
> хитрой конструкцией из ROW_NUMBER'ов летает и на таких количествах.
> файрберд тупил и на гораздо меньших цифрах, но там, возможно, я с
> индеками перемудрил.


Это что за конструкция такая ?

Вообще, как вы узнаете ROW_NUMBER() строки априори, не выполняя запроса ?
Posted via RSDN NNTP Server 2.1 beta
Re[8]: определить позицию строки в запросе
От: ov  
Дата: 31.08.09 11:37
Оценка:
MZ>Это что за конструкция такая ?
что-то типа этого. сервера под рукой нет проверить точно:
SELECT * FROM (SELECT *,ROW_NUMBER() OVER (ORDER BY ...) AS ROW FROM ...)
WHERE ROW>=... AND ROW<...


MZ>Вообще, как вы узнаете ROW_NUMBER() строки априори, не выполняя запроса ?

запрос, конечно, выполняется. но внутри БД и я не задумываюсь как его наиболее оптимально выполнить с ее точки зрения.
Re[9]: определить позицию строки в запросе
От: niteshade123  
Дата: 01.09.09 09:33
Оценка:
Здравствуйте, ov, Вы писали:

ov>что-то типа этого. сервера под рукой нет проверить точно:

ov>
ov>SELECT * FROM (SELECT *,ROW_NUMBER() OVER (ORDER BY ...) AS ROW FROM ...)
ov>WHERE ROW>=... AND ROW<...
ov>


это аналитика
если был insert/update/delete между двумя выполнениями этого запроса, то номер строки — это средняя температура по больнице
Вам ясно сказали: по ID
про генераторы/сиквенсы для нумерации результатов упоминать не будем, т.к. за такое в любой софтостроительной конторе рвут руки, и это справедливо
Re[10]: определить позицию строки в запросе
От: ov  
Дата: 01.09.09 13:24
Оценка:
N>Вам ясно сказали: по ID
про ID речь шла в контексте поиска выделенной строки после применения новой сортировки.
а пример запроса с ROW_NUMBER этого всего лишь эмуляция окна в MS SQL сервере. возможно кривая, я не вникал подробно, т.к. Compact Edition ее не поддерживает.

поиск выделенной строки по ID после сортировки у меня прекрасно работает пока я считываю результат запроса полностью.
а я хочу читать блоками, чтобы не фетчить все 100К/500К записей разом.
а после смены сортировки неплохо бы еще и блок правильный найти с первого раза.
и тут, похоже, все очень запущено...

N>про генераторы/сиквенсы для нумерации результатов упоминать не будем, т.к. за такое в любой софтостроительной конторе рвут руки, и это справедливо

согласен.
Re[11]: определить позицию строки в запросе
От: MasterZiv СССР  
Дата: 01.09.09 14:19
Оценка:
ov пишет:

> поиск выделенной строки по ID после сортировки у меня прекрасно работает

> пока я считываю результат запроса полностью.
> а я хочу читать блоками, чтобы не фетчить все 100К/500К записей разом.
> а после смены сортировки неплохо бы еще и блок правильный найти с
> первого раза.
> и тут, похоже, все очень запущено...

Ещё раз -- вы хотите невозможного. Почти невозможного.

Фетчить наборы данных можно только целиком. Вообще, если вы выбрали
набор, потом тот же запросы выполнили, и выбрали набор ещё раз --
это уже другой набор, к первому не имеющий никакого отношения.

Фетчить большие наборы тоже плохо -- они никому не нужны, даже отсортированные.
Вам следует не заниматься поисками эфимерности, а улучшить
критерии выборки, чтобы сократить кол-во выбераемых с сервера
данных.
Posted via RSDN NNTP Server 2.1 beta
Re[13]: определить позицию строки в запросе
От: ov  
Дата: 01.09.09 19:05
Оценка:
KRA>Может я чего не понимаю, но чем плох вариант:
вариант хорош. плохо то, что нет "настольной" БД, способной это сделать не подохнув...

KRA>Обычно пользователи хотят задавать любые критерии и иметь пейджинг.

угу. об этом и речь.
Re[13]: определить позицию строки в запросе
От: MasterZiv СССР  
Дата: 01.09.09 20:41
Оценка:
KRA пишет:

> Может я чего не понимаю, но чем плох вариант: сначала определяем на

> какую страницу попадает выбраная пользователем строка (по сути то с чего
> началась тема — нужно определить порядковый номер записи с даным
> идентификатором). Запрос получается вида (пишу с ораклиным синтаксисом)
>
>
> select rn from (
> select e.*, rownum rn
> from table e
> order by
> e.field1, e.field2
> )
> where id = :id
>
>
>
> при наличии индексов по полям сортировки даже при 100к может работать с
> приемлимой скоростью на соответствующем железе (по крайней мере оракл с
> rownum-ами неплохо работает).

Вот этот запрос:

select e.*, rownum rn
> from table e
> order by
> e.field1, e.field2
> )

который, между прочим, у вас всю таблицу обрабатывает,
должен сначала весь выбраться, отсортироваться,
и потом выбраться из него уже с id = :id.

Что тут может быть быстрого ?
Где тут поводы для оптимизации ? (кроме оптимизации ORDER BY
по индексу)

Кстати, по идее, order by в подзапросе СУБД может запросто
выкинуть, оно ни на что не влияет. Псевдоколонка rownum
же имеет смысл только в строке _возвращаемого_ набора данных,
она по идее там и должна генерироваться. Я не говорю, что
оракл так будет делать, просто логически тут всё очешь шатко.


> После этого, зная номер записи (и расчитав на какой странице она

> находится),

Как ?

мы можем подчитать только нужную нам страницу запросом вида
>
> select e.* from (
> select e.*, rownum rn
> from table e
> order by
> e.field1, e.field2
> )
> where rn between :start_index and :end_index
>
>
>
> Опять же в оракле такой запрос не приводит к тому, что читаются все
> данные целиком.

Ээээ.. доказательства будут ?

Оракл понимает, что такое rownum и как тут можно
> оптимизировать.

Как можно что-то оптимизировать по rownum, если он генерируется
в момент создания набора данных ?
Объясните, я не очень понимаю.

>

> Естественно оба запроса в одной транзакции.

> В теории согласен. Но вот на практике как это реализовать непонятно.

> Делать предварительную выборку количества без учёта сортировок и если в
> результате получается много — говорить пользователю, чтоб задал более
> жёсткие ограничения?

Да, например. Можно по любому запросу выводить, скажем, только первые 100
записей, и если есть ещё, писать, что часть данных были отброшены.

Больше 100-1000 записей никто читать не будет. Если это не выгрузка
данных куда-то для экспорта, или не отчёт какой-то для складирования, бесполезно
выводить более 100-1000 записей. Конкретные 100-1000 конечно варьируются в
зависимости от специфики приложения, но в общем идея такая.

Обычно пользователи хотят задавать любые критерии и
> иметь пейджинг.

Нахрена пейджинг нужен, если ты получаешь 10-100 нужных тебе записей ?
Posted via RSDN NNTP Server 2.1 beta
Re[13]: определить позицию строки в запросе
От: niteshade123  
Дата: 02.09.09 05:49
Оценка:
Здравствуйте, KRA, Вы писали:
KRA>

KRA>select rn from  (
KRA>select e.*, rownum rn
KRA>from table e
KRA>order by
KRA>  e.field1, e.field2
KRA>) 
KRA>where id = :id
KRA>

если так Вы хотите получить порядковый номер в сортировке e.field1, e.field2, то ошибаетесь

KRA>Опять же в оракле такой запрос не приводит к тому, что читаются все данные целиком. Оракл понимает, что такое rownum и как тут можно оптимизировать.

никакой магии
данные читаются целиком
Re[13]: определить позицию строки в запросе
От: Овощ http://www.google.com
Дата: 02.09.09 06:40
Оценка:
Здравствуйте, KRA, Вы писали:

KRA>Запрос получается вида (пишу с ораклиным синтаксисом)


KRA>
KRA>select rn from  (
KRA>select e.*, rownum rn
KRA>from table e
KRA>order by
KRA>  e.field1, e.field2
KRA>) 
KRA>where id = :id
KRA>


KRA>при наличии индексов по полям сортировки даже при 100к может работать с приемлимой скоростью на соответствующем железе (по крайней мере оракл с rownum-ами неплохо работает).


Правильно уже сказали — тут фактическая ошибка. Oracle высчитывает rownum до выполнения order by. Т.е. в общем случае надо делать так (выносить rownum на уровень выше чем order by):

select t.*, rownum
from
(
    select * from table1 order by field1
) t


KRA>После этого, зная номер записи (и расчитав на какой странице она находится), мы можем подчитать только нужную нам страницу запросом вида


KRA>
KRA>select e.* from (
KRA>select e.*, rownum rn
KRA>from table e
KRA>order by
KRA>  e.field1, e.field2
KRA>)
KRA>where rn between :start_index and :end_index
KRA>


Опять же не совсем правильно. О том как работает rownum и как правильно делать серверный пейджинг в оракле есть хорошая статья Ask Tom: On ROWNUM and Limiting Results.

KRA>Опять же в оракле такой запрос не приводит к тому, что читаются все данные целиком. Оракл понимает, что такое rownum и как тут можно оптимизировать.


Также здесь я не уверен, что такое использование значения rownum в конструкции between может быть эффективно оптимизировано. Oracle может построить эффективный план для простых предикатов, подобных ... where rownum < :end_index (и использовать в этом месте такую операцию в плане как COUNT STOPKEY). Впрочем, вероятно и это вполне может хорошо работать.
Re[15]: определить позицию строки в запросе
От: MasterZiv СССР  
Дата: 02.09.09 08:57
Оценка:
Овощ пишет:

> Однако полной сортировки не происходит, ввиду того что Oracle опять

> распознал использование rownum и понял что ему нужно лишь определенное
> фиксированное количество записей (100). Происходит это в операции SORT
> ORDER BY STOPKEY (filter(ROWNUM<=100)). Эта операция выделяет лишь
> небольшой буфер в памяти (у нас — на 100 элементов) и организует в нем
> что-то типа очереди с приоритетами. По мере того как будут читаться
> записи из таблицы, каждая запись будет сравниваться с теми записями,
> которые уже находятся в буфере, и оцениваться на то, попадает ли она в
> этот буфер (возможно вытесняя из него другие записи, помещенные в него
> раньше) или же просто отбрасывает новые строки. В любом случае на выходе
> этой операции будет ровно 100 нужных нам элементов отсортированных в
> нужном нам порядке. Это видно по трассировке:

"Однако полной сортировки не происходит". Ну да, читается вся таблица,
а "полной сортировки не происходит".
Это -- просто специальный вид алгоритма сортировки, с урезанным
результатом, она дешевле полной сортировки, да, но всё равно это --
обработка всей таблицы/набора. Вы же сами написали:

"каждая запись будет сравниваться с теми записями,
которые уже находятся в буфере"

> При относительно небольших N такая операция будет значительно менее

> затратной по ресурсам чем полная сортировка таблицы.

Основные затраты сервера при формировании наборов -- чтение данных,
чтение строк. Да, сортировка особенно больших наборов накладна,
но это -- не то, что нужно минимизировать при работе с БД.
Нужно минимизировать кол-во чтений записей. Если записей
мало, сортировки не оказывают уже какого-то влияния на производительность.


> Некоторые выводы:

> 1) Для оптимизации необходимы индексы. Поскольку наибольшая скорость
> будет именно с использованием индексов вместо сортировок.

Подчеркну мысль: индексы для ORDER BY.

Теперь покритикую: если есть критерии поиска, индексы должны
использоваться для поиска, а не для ORDER BY. Тогда ваша мысль не
работает.
Posted via RSDN NNTP Server 2.1 beta
Re[14]: определить позицию строки в запросе
От: MasterZiv СССР  
Дата: 02.09.09 09:01
Оценка:
Овощ пишет:

> Также здесь я не уверен, что такое использование значения rownum в

> конструкции between может быть эффективно оптимизировано. Oracle может
> построить эффективный план для простых предикатов, подобных ... where
> rownum < :end_index (и использовать в этом месте такую операцию в плане
> как COUNT STOPKEY). Впрочем, вероятно и это вполне может хорошо работать.

Дело не в том, какая конструкция, between или нет. Я уверен, что between
уж как-нибудь оракл умеет обрабатывать правильно (он представляется
как начало и конец диапазона, field >= :beg and field <= :end),
а вот дело-то в том, что rownum не известен ДО выполнения запроса,
поэтому на его основе нельзя что-то оптимизировать в принципе.

Вообще rownum -- дебильная штука. Хотя и прикольная иногда.
Posted via RSDN NNTP Server 2.1 beta
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.