Как мне сделать топ 1 в Oracle?


251

Как мне сделать следующее?

select top 1 Fname from MyTbl

В Oracle 11g ?




3
Можете ли вы сказать нам порядок, в соответствии с которым вы хотите «топ 1»?
Эндрю Вульф

1
Прежде всего, вам никогда не следует полагаться на движок БД для этого. Если вы хотите узнать такие вещи, вставьте секвенсор. При этом гарантируется, что они будут пронумерованы в том порядке, в котором они были вставлены.
FlyingGuy

1
Очень полезный материал на эту тему use-the-index-luke.com/sql/partial-results/top-n-queries
Илья Масков

Ответы:


257

Если вы хотите просто первую выбранную строку, вы можете:

select fname from MyTbl where rownum = 1

Вы также можете использовать аналитические функции для заказа и взять верхний x:

select max(fname) over (rank() order by some_factor) from MyTbl

54
Это хорошо, если вы хотите только 1 ряд и не волнует какой. Если вам нужны определенные строки, например самая последняя запись, вам нужно выполнить сортировку в подвыборке, например, в ответе Vash. Oracle назначает rownums перед сортировкой.
Скотт Бейли

4
@ Скотт да это верно. И Патрик, хорошо, я думаю, что синтаксис неверен в этом. Это действительно должно быть удержание в памяти (dens_rank () last ...)
mcpeterson

2
Разница между первым и вторым примером заключается в том, что первый выбирает строку (любая строка без порядка). Второй пример получает значение первой строки, не выполняя внутренний запрос заказа (согласно примерам ниже).
JulesLt

3
Неправильный синтаксис в: выберите max (fname) over (rank () по порядку some_factor) из MyTbl
Стефан Гербер

1
@jclozano "не очень известная функциональность оракула" - С уважением, я позволю себе не согласиться. Это важно, потому что «недостаточно известная функциональность» имеет тенденцию подразумевать неясность и, следовательно, может указывать на то, что нам следует избегать использования. Это неясно, и его использование не следует избегать.
сентября

166
SELECT *
  FROM (SELECT * FROM MyTbl ORDER BY Fname )
 WHERE ROWNUM = 1;

8
Этот ответ правильно получает строку TOP (упорядочивает результаты до ограничения на ROWNUM).
JulesLt

Этот ответ не является точным переводом - исходный запрос не имеет ORDER BY и не возвращает все столбцы в таблице.
OMG Ponies

Я исправлен (см. Ниже). Поменяю голоса, когда время истечет.
JulesLt

7
@OMGPonies да. но это, вероятно, то, что на самом деле хотят большинство людей, которые приходят на эту страницу, прибегая к помощи своей проблемы
NimChimpsky

4
Это наверняка должен быть победный ответ в этой теме. Я мог бы добавить примечание, которое top Xможно изменить наWHERE ROWNUM <= X
SomethingSomething

31

С участием Oracle 12c (июнь 2013 г.) вы можете использовать его следующим образом.

SELECT * FROM   MYTABLE
--ORDER BY COLUMNNAME -OPTIONAL          
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY

6
Интересная команда, я использую здесь 12c, и, OFFSET 0 ROWSвидимо, в этом нет необходимости, вы можете использовать FETCH NEXT 1 ROWS ONLYили даже FETCH FIRST ROW ONLY, порядок по важен, или он будет эквивалентен простому использованию WHERE rownum = 1. Я даже пробовал это в инструкции OUTER APPLY, и она работала как функция TOP Ms-SQL там.
Рафаэль Мерлин

Вы правы @RafaelMerlin. После вашего поста я понял, что OFFSET 0 ROWS не нужен. Это было бы полезно при получении данных между вершиной X и вершиной Y.
MSK

1
Другие примеры: oracle-base.com/articles/12c/...
FixFaier

Пока все хорошо, с важным упущением TIES. Направьте это в тех случаях, когда связи происходят для версии 12c +и12c -
Barbaros Özhan

10

Вы можете использовать ROW_NUMBER()с ORDER BYпредложением в подзапросе и использовать этот столбец вместо TOP N. Это можно объяснить пошагово.

Смотрите таблицу ниже, у которой есть два столбца NAMEи DT_CREATED.

введите описание изображения здесь

Если вам нужно взять только первые две даты, независимо от того NAME, вы можете использовать следующий запрос. Логика была написана внутри запроса

-- The number of records can be specified in WHERE clause
SELECT RNO,NAME,DT_CREATED
FROM
(
    -- Generates numbers in a column in sequence in the order of date
    SELECT ROW_NUMBER() OVER (ORDER BY DT_CREATED) AS RNO,
    NAME,DT_CREATED
    FROM DEMOTOP
)TAB
WHERE RNO<3;

РЕЗУЛЬТАТ

введите описание изображения здесь

В некоторых ситуациях нам нужно выбрать TOP Nрезультаты, соответствующие каждому NAME. В таком случае мы можем использовать PARTITION BYс ORDER BYпредложением в подзапросе. Обратитесь к следующему запросу.

-- The number of records can be specified in WHERE clause
SELECT RNO,NAME,DT_CREATED
FROM
(
  --Generates numbers in a column in sequence in the order of date for each NAME
    SELECT ROW_NUMBER() OVER (PARTITION BY NAME ORDER BY DT_CREATED) AS RNO,
    NAME,DT_CREATED
    FROM DEMOTOP
)TAB
WHERE RNO<3;

РЕЗУЛЬТАТ

введите описание изображения здесь


1
Использование ROW_NUMBER () ... более правильное решение, чем в тематическом ответе. Одна проблема с этим решением (и с макс. (Полевым) вариантом), что вы не можете делать такие вещи, как «выберите ... (выберите ROW_NUMBER () ...) для обновления
Алексо По.

И это иногда очень важно в PL / SQL (извините, не удалось отредактировать предыдущий комментарий за 5 минут).
Алексо По.

В таком случае мы можем использовать CTE как во внешней части. Правильно? @ Алексо По.
Сарат Аванаву

Я думаю, что я вас не понимаю. Предложение for update можно использовать, когда ROWID «легко» сохраняется Oracle. Таким образом, группировка (и группировка из-за использования аналитического предложения) скрывает реальный ROWID, и строки не могут быть заблокированы. И, во-вторых, CTE ( with (select ... ) as предложение) ничего не меняет в этой проблеме, CTE просто стремится читать и поддерживать запросы. Правильно? @ Сарат Аванаву
Алексо По.

Обратите внимание на себя. Проблема с ROWID на самом деле происходит именно из-за того, что условие RNO <3 , в этом случае значение RNO не связано с ROWID, поэтому Oracle не может блокировать строки.
Алексо По.

9

Вы можете сделать что-то вроде

    SELECT *
      FROM (SELECT Fname FROM MyTbl ORDER BY Fname )
 WHERE rownum = 1;

Вы также можете использовать аналитические функции RANK и / или DENSE_RANK , но ROWNUM , вероятно, самый простой.




5

Использование:

SELECT x.*
  FROM (SELECT fname 
          FROM MyTbl) x
 WHERE ROWNUM = 1

При использовании Oracle9i + вы можете использовать аналитические функции, такие как ROW_NUMBER (), но они не будут работать так же хорошо, как ROWNUM .


1
Хороший ответ, но содержит крошечную опечатку. Где вы говорите, Oracle9i + не должно быть 8i? download-west.oracle.com/docs/cd/A87860_01/doc/server.817/…
Ян Карпентер

@carpenteri: Да, аналитика была доступна в 8i - не могу вспомнить подробности, но аналитика не была доступна широкой публике до 9i.
OMG Ponies

Небольшой комментарий - ответ Vash ниже включает ORDER BY на внутренний запрос, который имеет решающее значение, если вы хотите, чтобы значение TOP имело значение fname, а не 'first' (который может быть любым, скорее всего, вставлена ​​первая строка). Может быть стоит редактировать?
JulesLt

@JulesLt: запрос, предоставленный OP, не включает ORDER BY, поэтому этот ответ представляет собой точный перевод к синтаксису Oracle.
OMG Ponies

Мое недопонимание синтаксиса SQL SERVER TOP (ошибочно предположил, что он был похож на FIRST в RANK, а не ROWNUM). Проголосовал.
JulesLt

3

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

Первый

select  max(Fname) from MyTbl;

второй

select  min(Fname) from MyTbl;

Третий

select  Fname from MyTbl  where rownum = 1;

четвертый

select  max(Fname) from MyTbl where rowid=(select  max(rowid) from MyTbl)

3

У меня была та же проблема, и я могу исправить это с помощью этого решения:

select a.*, rownum 
from (select Fname from MyTbl order by Fname DESC) a
where
rownum = 1

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

Удачи

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.