Как ограничить количество строк, возвращаемых запросом Oracle после заказа?
есть ли способ сделать Oracle
запрос ведет себя так, как будто он содержит MySQL limit
предложения?
на MySQL
, Я могу сделать это:
select *
from sometable
order by name
limit 20,10
чтобы получить 21-й до 30-й строк (пропустите первые 20, дайте следующие 10). Строки выбираются после order by
, поэтому он действительно начинается с 20-го имени в алфавитном порядке.
на Oracle
, единственное, что люди упоминают это rownum
псевдо-столбец, но он оценивается до order by
, что означает это:
select *
from sometable
where rownum <= 10
order by name
вернет случайный набор из десяти строк, упорядоченных по имени, что обычно не то, что я хочу. Он также не позволяет указать смещение.
4 ответов
начиная с Oracle 12c R1 (12.1), там is a строка ограничительного пункта. Он не использует знакомые LIMIT
синтаксис, но он может сделать работу лучше, с большим количеством опций. Вы можете найти полный синтаксис здесь.
чтобы ответить на исходный вопрос, вот запрос:
SELECT *
FROM sometable
ORDER BY name
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
(для более ранних версий Oracle, пожалуйста, обратитесь к другим ответам в этом вопрос)
примеры:
следующие примеры были приведены из ссылке, в надежде предотвратить гниение ссылке.
настройка
CREATE TABLE rownum_order_test (
val NUMBER
);
INSERT ALL
INTO rownum_order_test
SELECT level
FROM dual
CONNECT BY level <= 10;
COMMIT;
что в таблице?
SELECT val
FROM rownum_order_test
ORDER BY val;
VAL
----------
1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
20 rows selected.
первый N
строки
SELECT val
FROM rownum_order_test
ORDER BY val DESC
FETCH FIRST 5 ROWS ONLY;
VAL
----------
10
10
9
9
8
5 rows selected.
первый N
строк если N
th строка имеет связи, получить все связанные строки
SELECT val
FROM rownum_order_test
ORDER BY val DESC
FETCH FIRST 5 ROWS WITH TIES;
VAL
----------
10
10
9
9
8
8
6 rows selected.
Top x
% от строки
SELECT val
FROM rownum_order_test
ORDER BY val
FETCH FIRST 20 PERCENT ROWS ONLY;
VAL
----------
1
1
2
2
4 rows selected.
используя смещение, очень полезно для разбиения на страницы
SELECT val
FROM rownum_order_test
ORDER BY val
OFFSET 4 ROWS FETCH NEXT 4 ROWS ONLY;
VAL
----------
3
3
4
4
4 rows selected.
вы можете комбинировать смещение с процентами
SELECT val
FROM rownum_order_test
ORDER BY val
OFFSET 4 ROWS FETCH NEXT 20 PERCENT ROWS ONLY;
VAL
----------
3
3
4
4
4 rows selected.
вы можете использовать подзапрос для этого, как
select *
from
( select *
from emp
order by sal desc )
where ROWNUM <= 5;
Также посмотрите на тему на ROWNUM и ограничивающие результаты в Oracle/AskTom для получения дополнительной информации.
обновление: Чтобы ограничить результат как нижними, так и верхними границами, вещи становятся немного более раздутыми с
select * from
( select a.*, ROWNUM rnum from
( <your_query_goes_here, with order by> ) a
where ROWNUM <= :MAX_ROW_TO_FETCH )
where rnum >= :MIN_ROW_TO_FETCH;
(скопировано с указанного AskTom-article)
обновление 2: Начиная с Oracle 12c (12.1) существует синтаксис, доступный для ограничьте строки или начните с смещений.
SELECT *
FROM sometable
ORDER BY name
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
посмотреть ответ для получения дополнительных примеров. Благодаря Krumia за подсказку.
Я провел тестирование производительности для следующих подходов:
Asktom
select * from (
select a.*, ROWNUM rnum from (
<select statement with order by clause>
) a where rownum <= MAX_ROW
) where rnum >= MIN_ROW
аналитическая
select * from (
<select statement with order by clause>
) where myrow between MIN_ROW and MAX_ROW
Короткое Альтернатива
select * from (
select statement, rownum as RN with order by clause
) where a.rn >= MIN_ROW and a.rn <= MAX_ROW
результаты
таблица имела 10 миллионов записей, сортировка была на неиндексированной строке datetime:
- объяснить план показал одинаковое значение для всех трех вариантов (323168)
- но победителем является AskTom (с аналитическим следующим закрытием сзади)
Выбор первых 10 строк взял:
- AskTom: 28-30 секунд
- аналитическая: 33-37 секунд
- короткая альтернатива: 110-140 секунд
выбор строк между 100,000 и 100,010:
- AskTom: 60 секунд
- аналитический: 100 секунд
выбор строк между 9,000,000 и 9,000,010:
- AskTom: 130 секунды!--17-->
- аналитический: 150 секунд
аналитическое решение с одним вложенным запросом:
SELECT * FROM
(
SELECT t.*, Row_Number() OVER (ORDER BY name) MyRow FROM sometable t
)
WHERE MyRow BETWEEN 10 AND 20;
Rank()
можно заменить на Row_Number()
но может вернуть больше записей, чем вы ожидаете, если есть повторяющиеся значения для имени.