Як зробити топ-1 в Oracle?


251

Як зробити наступне?

select top 1 Fname from MyTbl

В Oracle 11г ?




3
Чи можете ви сказати нам про порядок, згідно з яким ви хочете "топ-1"?
Ендрю Вулф

1
Перш за все, ви ніколи не повинні покладатися на механізм БД, щоб це зробити. Якщо ви хочете дізнатися подібні речі, покладіть в секвенсор. Коли ви це зробите, гарантується, що вони будуть пронумеровані в тому порядку, в який вони були вставлені.
FlyingGuy

1
Дуже корисний матеріал на цю тему use-the-index-luke.com/sql/partial-results/top-n-queries
Ілля Масков

Відповіді:


257

Якщо ви хочете лише перший вибраний рядок, ви можете:

select fname from MyTbl where rownum = 1

Ви також можете використовувати аналітичні функції, щоб замовити та взяти верхній х:

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

54
Це добре, якщо ви хочете лише 1 ряд, і все одно, який. Якщо вам потрібні конкретні рядки, як, наприклад, останній запис, вам потрібно зробити сортування в підвідділі, як відповідь Ваша. Oracle призначає роунди перед сортом.
Скотт Бейлі

4
@Scott yup. це правильно. І Патрік, добре, я думаю, що синтаксис у цьому неправильний. Це дійсно повинно бути тримати над (щільний_rank () останній ...)
mcpeterson

2
Різниця між першим та другим прикладом полягає в тому, що перший вибирає рядок (будь-який рядок, без порядку). Другий приклад отримує значення першого рядка, не роблячи внутрішнього запиту замовлення (як показано нижче в прикладах).
JulesLt

3
Синтаксис не правильний: виберіть max (fname) over (rank () замовлення some_factor) з MyTbl
Stéphane Gerber

1
@jclozano "не дуже відома функціональність оракула" - З повагою я прошу відрізнятися. Це має значення, тому що "недостатньо відома функціональність", як правило, передбачає незрозумілість, і, отже, можна запропонувати нам уникати використання. Це не є малозрозумілим, і його використання не слід уникати.
Сепстер

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

8
Ця відповідь правильно отримує рядок TOP (впорядковує результати перед обмеженням ROWNUM).
JulesLt

Ця відповідь не є точним перекладом - оригінальний запит не має ЗАМОВЛЕННЯ, а також не повертає всі стовпці таблиці.
OMG Ponies

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

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

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 () ... - правильніше рішення, ніж відповідь теми. Одна проблема з цим рішенням (і з варіантом max (поле) також), що ви не можете робити такі речі, як "select ... (select ROW_NUMBER () ...)" для оновлення ; "
Олексо По.

І це іноді дуже важливо в PL / SQL (вибачте, не вдалося відредагувати попередній коментар за 5 хвилин обмеження).
Олексо По.

У такому випадку ми можемо використовувати CTE, як у зовнішній частині. Правильно? @Alexo Po.
Сарат Аванаву

Я думаю, що я вас не розумію. застереження для оновлення можна використовувати, коли ROWID "легко" зберігається Oracle. Таким чином, групування (і групування за рахунок використання аналітичних пропозицій) приховує реальний ROWID, а рядки не можна заблокувати. По-друге, CTE ( with (select ... ) as пункт) нічого не змінює до цієї проблеми, CTE просто спрямований на читання та підтримку запитів. Правильно? @Sarath Avanavu
По.

Зверніть увагу на себе. Проблема з ROWID насправді виникає конкретно через те, що умова RNO <3 , в цьому випадку значення RNO не пов'язане з ROWID, тому Oracle не може блокувати рядки.
Олексо По.

9

Можна зробити щось на кшталт

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

Ви також можете використовувати аналітичні функції RANK та / або DENSE_RANK , але ROWNUM , мабуть, найпростіший.


1
чи можете ви допомогти з прикладом рангу тощо
breakfreehg


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/…
Ian Carpenter

@carpenteri: Щоправда, аналітика була доступна в 8i - не можу згадати подробиці, але аналітика не була доступною для публіки до 9i.
OMG Ponies

Невеликий коментар - Відповідь Ваша нижче містить ЗАМОВЛЕННЯ НА внутрішній запит, що є критичним, якщо ви хочете, щоб ТОП значення імені, а не "перший" (це може бути що завгодно, швидше за все, вставлений перший рядок). Можливо, варто редагувати?
JulesLt

@JulesLt: Запит, який надає ОП, не містить ЗАМОВЛЕННЯ 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.