Несколько лет назад проект в моей компании попробовал именно то, что вы делаете. Не только их попытка не удалась, но и проект не удался.
Вот некоторые из опасностей, с которыми вы можете столкнуться:
- Данные изменяются во время обработки, и все не обрабатывается.
- Поскольку у вас нет
ORDER BY
в вашем SELECT
, вы получаете несколько строк дважды, а некоторые - совсем нет.
- Если у вас нет нужных индексов, каждый запрос выполняет полное сканирование таблицы.
- Если вы используете индекс, запрос по-прежнему отправляется в таблицу для всех строк, включая нужные вам строки. Вероятно, поэтому при просмотре страниц запрос замедляется.
Я настоятельно рекомендую вам пересмотреть свое общее решение, чтобы выполнить обработку в базе данных, если вы можете. Используйте DBMS_PARALLEL_EXECUTE, если вам нужна обработка "сделай сам" (я думаю, что требуется версия 11.2).
Я все равно постараюсь ответить на ваш вопрос в том виде, в котором его спросили.
Большинство требований к нумерации страниц отличаются от ваших! Они хотят отправить первые несколько страниц в пользовательский интерфейс. Вы хотите разбить все активные строки на страницы. Самый эффективный способ сделать это все сразу. Например:
create table t ( id, active, val) as
with actives(active) as (
select 'Y' from dual union all
select null from dual union all
select null from dual
)
, vals(val) as (
select level from dual
connect by level <= 1000
)
select rownum, active, val
from actives, vals,
(select null from dual connect by level <= 1000);
select count(*) from t;
COUNT(*)
---------
3000000
Теперь создайте промежуточную таблицу с одной строкой на страницу, указывая диапазон ROWID для каждой страницы.
create table pages(page primary key, start_rowid, end_rowid) as
select * from (
select * from (
select rowidtochar(rowid) rid,
ceil(row_number() over(order by rowid) / 1000) page,
mod(row_number() over(order by rowid), 1000) is_start
from t
where active = 'Y'
)
where is_start in (1, 0)
)
pivot(max(rid) "ROWID" for is_start in (1 as "START", 0 as "END"));
Это создало 1000 строк для 1000 страниц за 1 секунду. Теперь посмотрим, сколько активных строк на последней странице.
select /*+ gather_plan_statistics */ count(*) from t t
join pages p
on t.active = 'Y'
and p.page = 1000
and t.rowid between p.start_rowid and p.end_rowid;
COUNT(*)
----------
1000
Это заняло менее 1/100 секунды.
Вот план выполнения этого запроса. Обратите внимание на шаг TABLE ACCESS BY ROWID RANGE и небольшое количество доступных буферов.
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | Buffers |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 | 13 |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 | 13 |
| 2 | NESTED LOOPS | | 1 | 2500 | 1000 | 13 |
| 3 | TABLE ACCESS BY INDEX ROWID| PAGES | 1 | 1 | 1 | 3 |
| 4 | INDEX UNIQUE SCAN | SYS_C0012519 | 1 | 1 | 1 | 2 |
| 5 | TABLE ACCESS BY ROWID RANGE| T | 1 | 2500 | 1000 | 10 |
-------------------------------------------------------------------------------------------