Как выбрать первые N записей из базы данных, содержащей миллион записей?

У меня есть база данных oracle, заполненная миллионами записей. Я пытаюсь написать SQL-запрос, который возвращает первые N" отсортированных записей ( скажем 100 записей) из базы данных, основанные на определенных условиях.

SELECT * 
FROM myTable 
Where SIZE > 2000 
ORDER BY NAME DESC

затем программно выберите первые N записей.

проблема с этим подходом является :

  • результаты запроса в полмиллиона записи и причины "порядок по имени" все записи, которые будут отсортированы по имени в порядке убывания. Эта сортировка занимает много времени. (почти на 30-40 секунд. Если я опущу ORDER BY, это займет всего 1 секунду).
  • после того, как вид меня интересует только первые N (100) записей. Поэтому сортировка полных записей бесполезна.

мои вопросы:

  1. можно ли указать 'N' в сам запрос? ( это касается только N записей и запрос будет быстрее).
  2. любой лучший способ в SQL улучшить запрос для сортировки только Н элементы и возвращение в быстром время.

5 ответов


Если ваша цель-найти 100 случайных строк и сортировать их после Лассе!--5--> является правильным. Если, как я думаю, вы хотите, чтобы первые 100 строк были отсортированы по имени, отбрасывая другие, вы построили бы такой запрос:

SELECT * 
  FROM (SELECT * 
          FROM myTable 
         WHERE SIZE > 2000 ORDER BY NAME DESC) 
 WHERE ROWNUM <= 100

оптимизатор поймет, что это запрос TOP-N и сможет использовать индекс по имени. Ему не нужно будет сортировать весь результирующий набор, он просто начнется в конце индекса и прочитает его назад и остановится после 100 строк.

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

SELECT /*+ FIRST_ROWS*/* FROM myTable WHERE SIZE > 2000 ORDER BY NAME DESC

Edit: просто добавив AND rownum <= 100 запрос не будет работать, так как в Oracle rownum приписывается до сортировка : вот почему вы должны использовать подзапрос. Без подзапроса Oracle выберет 100 случайных строк, а затем отсортирует их.


этой показывает, как выбрать первые n строк в зависимости от версии Oracle.

начиная с Oracle 9i, ранг () и Функции DENSE_RANK() можно использовать для определите верхние N строк. Примеры:

получите 10 лучших сотрудников на основе их жалованье!--3-->

выберите ename, sal FROM ( выбрать эмаль кулон, Сэл, ранг() над (приказ по АЛГ DESC) sal_rank От emp) где sal_rank

выберите сотрудников, входящих в топ-10 заработная плата

выберите ename, sal FROM ( выбрать ename, sal, DENSE_RANK() OVER (ORDER BY sal DESC) sal_dense_rank Из emp) где sal_dense_rank

разница между ними объясняется здесь


добавить это:

 AND rownum <= 100

к вашему предложению WHERE.

однако, это не будет делать то, что вы просите.

Если вы хотите выбрать 100 случайных строк, отсортировать их, а затем вернуть их, вам придется сначала сформулировать запрос без порядка, затем ограничить его 100 строками, затем выбрать из этого и отсортировать.

этой мог бы работа, но, к сожалению, у меня нет сервера Oracle, доступного для тестирования:

SELECT *
FROM (
    SELECT *
    FROM myTable
    WHERE SIZE > 2000
      AND rownum <= 100
    ) x
ORDER BY NAME DESC

но обратите внимание на "случайная" часть там, вы говорите: "Дайте мне 100 строк с размером > 2000, мне все равно, какие 100".

Это действительно то, чего вы хотите?

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


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

(Если столбец nullable, это все еще возможно, либо (a) добавив не нулевой предикат к запросу, либо (b) добавив индекс на основе функции и соответствующим образом изменив предложение ORDER BY).


просто для справки, в Oracle 12c эта задача может быть выполнена с помощью FETCH предложения. Вы можете видеть здесь для примеров и дополнительных ссылок на ссылки по этому вопросу.