Как ограничить количество строк, возвращаемых запросом Oracle после упорядочивания? - PullRequest
913 голосов
/ 22 января 2009

Есть ли способ заставить запрос 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

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

Ответы [ 14 ]

751 голосов
/ 22 января 2009

Вы можете использовать подзапрос для этого, как

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)

Обновление 2 : Начиная с Oracle 12c (12.1), существует синтаксис, позволяющий ограничивать строки или начинать со смещений.

SELECT * 
FROM   sometable
ORDER BY name
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

См. этот ответ для дополнительных примеров. Спасибо Крумии за подсказку.

454 голосов
/ 26 сентября 2014

Начиная с Oracle 12c R1 (12.1), равно 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.

Верх 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.
173 голосов
/ 30 июня 2011

Я провел тестирование производительности для следующих подходов:

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 миллионов записей, сортировка выполнялась по неиндексированной строке даты и времени:

  • План объяснения показал одинаковое значение для всех трех вариантов (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 секунд
  • Аналитические: 150 секунд
53 голосов
/ 23 января 2009

Аналитическое решение только с одним вложенным запросом:

SELECT * FROM
(
   SELECT t.*, Row_Number() OVER (ORDER BY name) MyRow FROM sometable t
) 
WHERE MyRow BETWEEN 10 AND 20;

Rank() может быть заменено на Row_Number(), но может вернуть больше записей, чем вы ожидаете, если для имени есть повторяющиеся значения.

28 голосов
/ 24 сентября 2013

В Oracle 12c (см. Пункт ограничения строки в Справочник по SQL ):

SELECT * 
FROM sometable
ORDER BY name
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
11 голосов
/ 12 апреля 2014

Запросы на нумерацию страниц с упорядочением действительно сложны в Oracle.

Oracle предоставляет псевдостолбец ROWNUM, который возвращает число, указывающее порядок, в котором база данных выбирает строку из таблицы или набора объединенных представлений.

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

Более того, значение ROWNUM увеличивается только после того, как оно назначено.

Вот почему следующий запрос не возвращает строк:

 select * 
 from (select *
       from some_table
       order by some_column)
 where ROWNUM <= 4 and ROWNUM > 1; 

Первая строка результата запроса не передает предикат ROWNUM> 1, поэтому ROWNUM не увеличивается до 2. По этой причине ни одно значение ROWNUM не будет больше 1, следовательно, запрос не возвращает строк.

Правильно определенный запрос должен выглядеть так:

select *
from (select *, ROWNUM rnum
      from (select *
            from skijump_results
            order by points)
      where ROWNUM <= 4)
where rnum > 1; 

Узнайте больше о запросах на нумерацию страниц в моих статьях на Vertabelo blog:

6 голосов
/ 02 марта 2011

Меньше операторов SELECT. Кроме того, меньше потребляет производительность. Кредиты для: anibal@upf.br

SELECT *
    FROM   (SELECT t.*,
                   rownum AS rn
            FROM   shhospede t) a
    WHERE  a.rn >= in_first
    AND    a.rn <= in_first;
2 голосов
/ 12 апреля 2017
select * FROM (SELECT 
   ROW_NUMBER() OVER (ORDER BY sal desc),* AS ROWID, 
 FROM EMP ) EMP  where ROWID=5

больше значений узнать

select * FROM (SELECT 
       ROW_NUMBER() OVER (ORDER BY sal desc),* AS ROWID, 
     FROM EMP ) EMP  where ROWID>5

меньше, чем значения узнают

select * FROM (SELECT 
       ROW_NUMBER() OVER (ORDER BY sal desc),* AS ROWID, 
     FROM EMP ) EMP  where ROWID=5
2 голосов
/ 08 декабря 2016

Если вы не используете Oracle 12C, вы можете использовать запрос TOP N, как показано ниже.

SELECT *
 FROM
   ( SELECT rownum rnum
          , a.*
       FROM sometable a 
   ORDER BY name
   )
WHERE rnum BETWEEN 10 AND 20;

Вы можете даже переместить это из предложения с помощью предложения следующим образом

WITH b AS
( SELECT rownum rnum
      , a.* 
   FROM sometable a ORDER BY name
) 
SELECT * FROM b 
WHERE rnum BETWEEN 10 AND 20;

Здесь на самом деле мы создаем встроенное представление и переименовываем rownum в rnum. Вы можете использовать rnum в основном запросе в качестве критерия фильтрации.

1 голос
/ 01 июня 2016

Я начал подготовку к экзамену Oracle 1z0-047, проверено на соответствие 12c Готовясь к нему, я столкнулся с улучшением 12c, известным как 'FETCH FIRST' Это позволяет вам извлекать строки / ограничивать строки в соответствии с вашим удобством. Несколько вариантов доступны с этим

- FETCH FIRST n ROWS ONLY
 - OFFSET n ROWS FETCH NEXT N1 ROWS ONLY // leave the n rows and display next N1 rows
 - n % rows via FETCH FIRST N PERCENT ROWS ONLY

Пример:

Select * from XYZ a
order by a.pqr
FETCH FIRST 10 ROWS ONLY
...