Как выбрать первые 'N' записей из базы данных, содержащей миллион записей? - PullRequest
8 голосов
/ 11 сентября 2009

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

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

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

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

  • В результате запроса получается полмиллиона записи и причины "ЗАКАЗАТЬ ПО ИМЯ" все записи должны быть отсортированы по NAME в порядке убывания. Эта сортировка занимает много времени. (почти 30-40 секунд. Если я опущу ORDER BY, это займет всего 1 секунду).
  • После сортировки меня интересует только первые N (100) записей. Поэтому сортировка полных записей бесполезна.

Мои вопросы:

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

Ответы [ 5 ]

19 голосов
/ 11 сентября 2009

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

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

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

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

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

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

5 голосов
/ 11 сентября 2009

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

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

Получите 10 лучших сотрудников на основе их зарплата

ВЫБРАТЬ эмаль, соль ОТ (ВЫБРАТЬ ename, sal, RANK () НАД (ЗАКАЗАТЬ НА САЛЮ DESC) sal_rank ОТ emp) ГДЕ sal_rank <= 10; </p>

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

ВЫБРАТЬ эмаль, соль ОТ (ВЫБРАТЬ ename, sal, DENSE_RANK () OVER (ЗАКАЗАТЬ BY sal DESC) sal_dense_rank ОТ emp) ГДЕ sal_dense_rank <= 10; </p>

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

4 голосов
/ 11 сентября 2009

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

 AND rownum <= 100

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

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

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

Это может работать, но, к сожалению, у меня нет сервера Oracle для тестирования:

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

Но обратите внимание на «случайную» часть, вы говорите «дайте мне 100 строк с размером> 2000, мне все равно, какие 100».

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

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

0 голосов
/ 27 декабря 2015

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

0 голосов
/ 14 сентября 2009

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

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

...