Я использую Oracle 10 г и следующую парадигму для получения страницы 15 результатов как время (так, чтобы, когда пользователь смотрит на страницу 2 результата поиска, они видели записи 16-30).
select *
from
( select rownum rnum, a.*
from (my_query) a
where rownum <= 30 )
where rnum > 15;
Прямо сейчас я должен выполнить отдельный SQL-оператор для проведения "избранного подсчета" на "my_query" для получения общего количества результатов для my_query (так, чтобы я мог показать его пользователю и использовать его для выяснения общего количества страниц, и т.д.).
Там какой-либо путь состоит в том, чтобы получить общее количество результатов, не делая этого через второй запрос, т.е. путем получения его от вышеупомянутого запроса? Я попытался добавить "макс. (rownum)", но это, кажется, не работает (я получаю ошибку [РТЫ 01747], который, кажется, указывает, что этому не нравлюсь я имеющий ключевое слово rownum в группе).
Мое объяснение для желания получить это из исходного запроса вместо того, чтобы делать его в отдельном SQL-операторе - то, что "my_query" является дорогим запросом, таким образом, я скорее не выполнил его дважды (однажды для получения количества, и однажды получить страницу данных), если я не имею к; но безотносительно решения я могу придумать для получения, количество результатов из единого запроса (и в то же время получают страницу данных, в которых я нуждаюсь), не должен добавлять много если любые дополнительные издержки, если это возможно. Советуйте.
Вот точно, что я пытаюсь сделать, для которого я получаю РТЫ 01 747 ошибок, потому что я полагаю, что им не нравлюсь я имеющий ROWNUM в группе. Отметьте, Если существует другое решение, которое не использует макс. (ROWNUM), но что-то еще, которое прекрасно подходит также. Этим решением была моя первая мысль относительно того, что могло бы работать.
SELECT * FROM (SELECT r.*, ROWNUM RNUM, max(ROWNUM)
FROM (SELECT t0.ABC_SEQ_ID AS c0, t0.FIRST_NAME, t0.LAST_NAME, t1.SCORE
FROM ABC t0, XYZ t1
WHERE (t0.XYZ_ID = 751) AND
t0.XYZ_ID = t1.XYZ_ID
ORDER BY t0.RANK ASC) r WHERE ROWNUM <= 30 GROUP BY r.*, ROWNUM) WHERE RNUM > 15
---------РЕДАКТИРОВАНИЕ--------Отметьте, на основе первого комментария, я попробовал следующий, который, кажется, работает. Я не знаю, как хорошо это работает по сравнению с другими решениями, хотя (я ищу решение, что fufills мое требование, но выполняет лучшее). Например, когда я выполняю это, требуется 16 секунд. Когда я вынимаю КОЛИЧЕСТВО (*) ПО () RESULT_COUNT, требуется всего 7 секунд:
SELECT * FROM (SELECT r.*, ROWNUM RNUM, )
FROM (SELECT COUNT(*) OVER () RESULT_COUNT,
t0.ABC_SEQ_ID AS c0, t0.FIRST_NAME, t1.SCORE
FROM ABC t0, XYZ t1
WHERE (t0.XYZ_ID = 751) AND t0.XYZ_ID = t1.XYZ_ID
ORDER BY t0.RANK ASC) r WHERE ROWNUM <= 30) WHERE RNUM > 1
Объяснить план изменяется от выполнения ВИДА (КЛЮЧ ОСТАНОВКИ ORDER BY), чтобы сделать ОКНО (ВИД).
Прежде:
SELECT STATEMENT ()
COUNT (STOPKEY)
VIEW ()
SORT (ORDER BY STOPKEY)
NESTED LOOPS ()
TABLE ACCESS (BY INDEX ROWID) XYZ
INDEX (UNIQUE SCAN) XYZ_ID
TABLE ACCESS (FULL) ABC
После:
SELECT STATEMENT ()
COUNT (STOPKEY)
VIEW ()
WINDOW (SORT)
NESTED LOOPS ()
TABLE ACCESS (BY INDEX ROWID) XYZ
INDEX (UNIQUE SCAN) XYZ_ID
TABLE ACCESS (FULL) ABC
Я думаю, вам нужно изменить свой запрос на что-то вроде этого, чтобы получить всю необходимую информацию по «единственному» запросу.
SELECT *
FROM (SELECT r.*, ROWNUM RNUM, COUNT(*) OVER () RESULT_COUNT
FROM (SELECT t0.ABC_SEQ_ID AS c0, t0.FIRST_NAME, t1.SCORE
FROM ABC t0, XYZ t1
WHERE (t0.XYZ_ID = 751)
AND t0.XYZ_ID = t1.XYZ_ID
ORDER BY t0.RANK ASC) R)
WHERE RNUM between 1 and 15
Причина в том, что оконная функция COUNT (*) OVER ()
вычисляется после предложения WHERE
, следовательно, выдает не общее количество записей, а количество записей, которые удовлетворяют условию ROWNUM <= 30
.
Если вы не можете согласиться с производительностью этого запроса или выполнения двух отдельных запросов, возможно, вам следует подумать о решении, подобном предложенному FrustratedWithFormsDesigner в его / ее комментарии о кешировании количества записей. .
Если вы регулярно работаете с базами данных, я рекомендую вам получить копию SQL Cookbook . Это исключительная книга с множеством полезных советов.
Работает ли это?
select *
from
( select rownum rnum, a.*, b.total
from (my_query) a, (select count(*) over () total from my_query) b
where rownum <= 30 )
where rnum > 15;
Нет, вы не можете этого сделать, не выполнив запрос дважды или не запустив его один раз, а также извлекая и кэшируя все строки для их подсчета перед тем, как начать их отображение. И то и другое нежелательно, особенно если ваш запрос дорогостоящий или потенциально возвращает много строк.
Собственный инструмент Oracle Application Express (Apex) предлагает выбор вариантов разбивки на страницы:
Псевдо-PL / SQL для варианта 3 (по вашему выбору) будет выглядеть следующим образом:
l_total := 15;
for r in
( select *
from
( select rownum rnum, a.*
from (my_query) a
)
where rnum > 15
)
loop
l_total := l_total+1;
if runum <= 30 then
print_it;
end if;
end loop;
show_page_info (15, 30, l_total);
WITH
base AS
(
SELECT ROWNUM RNUM, A.*
FROM (SELECT * FROM some_table WHERE some_condition) A
)
SELECT FLOOR(((SELECT COUNT(*) FROM base) / 15) + 1) TOTAL_PAGES_TO_FETCH,
((ROWNUM - MOD(ROWNUM, 15)) / 15) + 1 PAGE_TO_FETCH,
B.*
FROM base B
Этот запрос вычислит, сколько групп страниц вам нужно будет получить, и получит данные как один запрос.
Из набора результатов обрабатывать 15 строк за раз. Самый последний набор строк может быть короче 15.
Просто предложение:
Вы можете рассмотреть подход Google "1-10 из примерно 13 000 000 результатов" - запустить COUNT(*) в качестве быстрой выборки по исходному запросу. Я предположил, что для данного ABC
существует не более одного XYZ
:
SELECT *
FROM (SELECT r.*, ROWNUM RNUM,
(SELECT COUNT(*) * 100
FROM ABC SAMPLE(1) t0
WHERE (t0.XYZ_ID = 751)
) RESULT_COUNT
FROM (SELECT t0.ABC_SEQ_ID AS c0, t0.FIRST_NAME, t1.SCORE
FROM ABC t0, XYZ t1
WHERE (t0.XYZ_ID = 751)
AND t0.XYZ_ID = t1.XYZ_ID
ORDER BY t0.RANK ASC) R)
WHERE RNUM between 1 and 15
Очевидно, что выборка будет довольно неточной и изменчивой, так что это зависит от требований, подходит это или нет.
Другим решением может быть создание материализованного представления, которое поддерживает подсчеты для каждого значения ABC.XYZ_ID
- таким образом вы перекладываете бремя получения подсчетов на процессы, которые вставляют/обновляют/удаляют строки в таблице.