Oracle SELECT TOP 10 записей

89

У меня большая проблема с SQL-выражением в Oracle. Я хочу выбрать TOP 10 записей, заказанных STORAGE_DB, которые не входят в список из другого оператора select.

Этот файл отлично работает для всех записей:

SELECT DISTINCT 
  APP_ID, 
  NAME, 
  STORAGE_GB, 
  HISTORY_CREATED, 
  TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') AS HISTORY_DATE  
  FROM HISTORY WHERE 
      STORAGE_GB IS NOT NULL AND 
        APP_ID NOT IN (SELECT APP_ID
                       FROM HISTORY
                        WHERE TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') = '06.02.2009') 

Но когда я добавляю

AND ROWNUM <= 10
ORDER BY STORAGE_GB DESC

Я получаю какие-то "случайные" записи. Я думаю, потому что предел принимает место перед порядком.

Есть ли у кого-то хорошее решение? Другая проблема: этот запрос очень медленный (записи 10k +)

Теги:

6 ответов

161
Лучший ответ

Вам нужно будет поместить ваш текущий запрос в подзапрос, как показано ниже:

SELECT * FROM (
  SELECT DISTINCT 
  APP_ID, 
  NAME, 
  STORAGE_GB, 
  HISTORY_CREATED, 
  TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') AS HISTORY_DATE  
  FROM HISTORY WHERE 
    STORAGE_GB IS NOT NULL AND 
      APP_ID NOT IN (SELECT APP_ID FROM HISTORY WHERE TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') ='06.02.2009')
  ORDER BY STORAGE_GB DESC )
WHERE ROWNUM <= 10

Oracle применяет rownum к результату после его возвращения.
Вам нужно отфильтровать результат после его возвращения, поэтому требуется подзапрос. Вы также можете использовать RANK() для получения результатов Top-N.

Для производительности попробуйте использовать NOT EXISTS вместо NOT IN. Подробнее см. .

  • 0
    NOT EXISTS не работает в этом сценарии (недопустимый реляционный оператор) APP_ID NOT EXISTS (SELEC ...)
  • 0
    благодарю вас! Отлично работает для Oracle 11g
Показать ещё 2 комментария
23

Что касается плохой работы, то существует множество вещей, которые могут быть, и это действительно должен быть отдельный вопрос. Однако есть одна очевидная вещь, которая может быть проблемой:

WHERE TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') = '06.02.2009') 

Если HISTORY_DATE действительно является столбцом даты, и если у него есть индекс, то этот переписать будет работать лучше:

WHERE HISTORY_DATE = TO_DATE ('06.02.2009', 'DD.MM.YYYY')  

Это связано с тем, что преобразование типа данных отключает использование индекса B-Tree.

13

Если вы используете Oracle 12c, используйте:

FETCH NEXT N ROWS ONLY

SELECT DISTINCT 
  APP_ID, 
  NAME, 
  STORAGE_GB, 
  HISTORY_CREATED, 
  TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') AS HISTORY_DATE  
  FROM HISTORY WHERE 
    STORAGE_GB IS NOT NULL AND 
      APP_ID NOT IN (SELECT APP_ID FROM HISTORY WHERE TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') ='06.02.2009')
  ORDER BY STORAGE_GB DESC
FETCH NEXT 10 ROWS ONLY

Дополнительная информация: http://docs.oracle.com/javadb/10.5.3.0/ref/rrefsqljoffsetfetch.html

  • 1
    Просто и по существу. ПОЛУЧИТЬ СЛЕДУЮЩИЕ 10 СТРОК ТОЛЬКО
  • 1
    это золото по сравнению с другим ответом
Показать ещё 2 комментария
10

Вы получаете явно произвольный набор, потому что ROWNUM применяется до ORDER BY. Таким образом, ваш запрос занимает первые десять строк и сортирует их. Чтобы выбрать первую десятку зарплат, вы должны использовать аналитическую функцию в подзапросе, а затем отфильтровать это:

 select * from 
     (select empno,
             ename,
             sal,
             row_number() over(order by sal desc nulls last) rnm
    from emp) 
 where rnm<=10
4

попробовать SELECT * FROM пользователей FETCH NEXT 10 ROWS ONLY;

  • 2
    Работает только на Oracle 12c и выше
-4

Вы можете просто использовать TOP Clause

ВЫБРАТЬ ТОП 10 * ОТ ТАБЛИЦЫ

Или

SELECT column_name (s) FROM table_name WHERE ROWNUM <= number;

Ещё вопросы

Сообщество Overcoder
Наверх
Меню