Як вибрати найнижчі рядки?


100

Я можу зробити SELECT TOP (200) ... але чому б не BOTTOM (200)?

Ну, щоб не вникати у філософію, що я маю на увазі, як я можу зробити еквівалент TOP (200), але зворотно (знизу, як би ви очікували, що БОТОМ буде робити ...)?

Відповіді:


89
SELECT
    columns
FROM
(
     SELECT TOP 200
          columns
     FROM
          My_Table
     ORDER BY
          a_column DESC
) SQ
ORDER BY
     a_column ASC

2
Чому ви використовуєте похідну таблицю?
RichardOD

14
Якщо ви хочете повернути рядки в порядку A-> Z, але виберіть топ 200 у Z-> A order - це один із способів. Інші відповіді, пропонуючи просто змінити ЗАМОВЛЕННЯ, не повернуть ті самі результати, що описані у запитанні, оскільки вони вийдуть з ладу (якщо порядок не має значення, про що ОП не сказала).
Том Н

3
@Tom H. Довелося подумати кілька секунд, що ти мав на увазі (я вже 14 годин). Спочатку я не бачив різниці між вашим та порядком у відповідях, але тепер можу. Так +1.
RichardOD

1
Цей та інші відповіді чудово працюють, коли ви працюєте на менших таблицях. Я не думаю, що варто упорядковувати всю таблицю стовпчиком, коли вас просто цікавлять кілька нижніх рядків.
стійловий риб

1
хороша відповідь, найкраще імхо ... справжня проблема полягає в тому, що я не можу зробити це за допомогою сценарію ASP, тому я думаю, що мені потрібно змінити порядок objRecordset вручну або з функцією, яку надає ASP ....
Andrea_86

98

Це зайве. Ви можете використовувати ORDER BYта просто змінити сорт, DESCщоб отримати такий же ефект.


3
google сказав те саме, і тепер 9 з вас згодні, досить добре для мене, дякую: D
CloudMeta

9
Використання DESC поверне останні N рядків, але повернуті рядки також будуть у зворотному порядку від перших N рядків.
RickNZ

11
Що робити, якщо на вашому столі немає індексу ЗАМОВИТИ?
Захисник один

8
@Justin: Уявіть, що у вас є лише один стовпець, який містить значення varchar. ЗАМОВЛЕННЯ буде сортувати за алфавітом, що (мабуть) не те, що ми хочемо.
Захисник 1

3
Том Х. - правильний козир, інакше ваші рядки будуть у зворотному порядку.
П’єр-Олів'є Гулет

39

Вибачте, але, на мою думку, я не бачу правильних відповідей.

Функція TOPx показує записи в невизначеному порядку. З цього визначення випливає, що BOTTOMфункцію неможливо визначити.

Незалежно від будь-якого індексу чи порядку сортування. Коли ви робите, ORDER BY y DESCви отримуєте рядки з найвищим значенням у. Якщо це автогенерований ідентифікатор, він повинен відображати записи, які останнім часом були додані до таблиці, як це запропоновано в інших відповідях. Однак:

  • Це працює лише за наявності автогенерованого стовпця id
  • Це має значний вплив на продуктивність, якщо порівнювати це з TOPфункцією

Правильна відповідь повинна полягати в тому, що немає та не може бути еквівалентом TOPдля отримання нижніх рядків.


3
Щоправда, начебто немає еквівалента, просто обхідні шляхи.
ткнути

4
TOP не має нічого спільного з порядком, коли елементи додані до таблиці, це просто означає "Наведіть перші X записи, які відповідають моєму запиту"
Лука

4
Так, це дає, ви отримуєте перші записи, додані до таблиці, які відповідають вашому запиту.
Martijn Burger

4
Я згоден з Люком. Таблиці баз даних за визначенням не мають порядку. Ніколи не слід покладатися на порядок, наданий RDBMS, коли у операторі select немає пункту ORDER BY. читати тут на wiki Однак система баз даних не гарантує впорядкування рядків, якщо в операторі SELECT, який запитує таблицю, не вказано пункт ORDER BY.
Зохар Пелед

2
Я згоден з цією відповіддю, але на практиці відповідь Тома вирішує проблему для всіх практичних цілей.
Антоніо

18

Логічно,

BOTTOM (x) is all the records except TOP (n - x), where n is the count; x <= n

Напр. Вибір дна 1000 від працівника:

У T-SQL,

DECLARE 
@bottom int,
@count int

SET @bottom = 1000 
SET @count = (select COUNT(*) from Employee)

select * from Employee emp where emp.EmployeeID not in 
(
SELECT TOP (@count-@bottom) Employee.EmployeeID FROM Employee
)

1
Привіт shadi2014, без використання "ЗАМОВЛЕННЯ" ваш результат буде якось випадковим.
буммі

5
bummi, ти маєш рацію, але те, що робить цю відповідь правильною. Сам вибір ТОП теоретично "випадковий", і це правильна реалізація для Select BOTTOM. У таблиці з 5000 записів знизу 1000 - це все, крім перших 4000.
tzachs

9

Здавалося б, у будь-якому з відповідей, які реалізують пункт ORDER BY у рішенні, відсутня точка, або насправді не розуміється, що TOP повертається до вас.

TOP повертає невпорядкований набір результатів запиту, який обмежує набір записів першими N поверненими записами. (З точки зору Oracle, це схоже на додавання ROWNUM <(N + 1).

Будь-яке рішення, яке використовує замовлення, може повертати рядки, які також повертаються пунктом TOP (оскільки цей набір даних в першу чергу був не упорядкованим), залежно від того, якими критеріями користувався в наказі

Корисність TOP полягає в тому, що як тільки набір даних досягає певного розміру N, він зупиняє отримання рядків. Ви можете відчути, як виглядають дані, не виймаючи їх.

Щоб точно реалізувати BOTTOM, потрібно буде отримати весь набір даних не упорядкованим, а потім обмежити набір даних до остаточних N записів. Це не буде особливо ефективно, якщо ви маєте справу з величезними таблицями. Також це не обов'язково дасть вам те, що, на вашу думку, ви просите. Кінець набору даних не обов'язково може бути "останніми вставленими рядками" (і, ймовірно, не буде для більшості інтенсивних програм DML).

Аналогічно, рішення, які реалізують ORDER BY, на жаль, можуть бути згубними при роботі з великими наборами даних. Якщо у мене, скажімо, 10 мільярдів записів і хочеться останніх 10, то досить нерозумно замовити 10 мільярдів записів і вибрати останні 10.

Проблема тут полягає в тому, що BOTTOM не має сенсу, про який ми думаємо, порівнюючи його з TOP.

Коли записи вставляються, видаляються, вставляються, видаляються знову і знову, знову, деякі пропуски з’являться у сховищі, а пізніше рядки будуть розміщені в місцях, якщо це можливо. Але те, що ми часто бачимо, коли ми вибираємо TOP, виявляється сортованими даними, оскільки вони, можливо, були вставлені на початку існування таблиці. Якщо таблиця не зазнає багатьох видалень, вона може з’явитися впорядкованою. (наприклад, дати створення можуть бути такими ж далекими від часу, як і самі створення таблиці). Але реальність полягає в тому, що якщо це велика таблиця для видалення, TOP N рядків може зовсім не виглядати так.

Отже - підсумок тут (призначений каламбур) полягає в тому, що той, хто запитує записи BOTTOM N, насправді не знає, про що вони просять. Або, принаймні, те, що вони просять і що BOTTOM насправді означає, - це не те саме.

Отже - рішення може задовольнити фактичну ділову потребу запитувача ..., але не відповідає критеріям БУТЬ.


1
Відмінне пояснення. Резюме для пролиття більше світла на цю тему.
rohrl77

Мій випадок використання такий. Я запустив велику insertзаяву, щоб помістити рядки у велику, недекларовану таблицю. (Я заповнюю таблицю спочатку, перш ніж почати її індексувати.) Я втратив сеанс свого клієнта через перезавантаження чи будь-що інше, і тепер я хочу перевірити, чи є у мене щойно додані рядки. Якщо "нижній" рядок таблиці є одним із моїх останніх, я знаю, що операція завершена. Якщо "нижній" рядок є чимось іншим, ну ніяких гарантій немає, і мені доведеться сканувати всю таблицю, щоб переконатися ... але, швидше за все, я міг би заощадити час, швидко перевіривши "дно" так само, як ви можете " верх '.
Ed Avis

Хороше пояснення, але воно все ж передбачає існування дна, що просто вимагає, щоб дані були прочитані / отримані в зворотному порядку. У (допустимому краю) випадку перерваної вставки в новій таблиці, перевірка останнього вставленого запису (внизу), не завантажуючи все було б корисно. Чи є технічна причина, чому дані таблиці не можна отримати у зворотному порядку?
Джеймс

3

На даний момент прийнята відповідь "Джастіна Етьє" не є правильною відповіддю, на що вказував "Протектор один".

Наскільки я бачу, на даний момент жодна інша відповідь чи коментар не забезпечує еквівалент BOTTOM (x) на запитання автора, який запитував.

Спочатку розглянемо сценарій, коли ця функціональність буде потрібна:

SELECT * FROM Split('apple,orange,banana,apple,lime',',')

Це повертає таблицю з одного стовпця та п'яти записів:

  • яблуко
  • помаранчевий
  • банан
  • яблуко
  • вапна

Як бачите: у нас немає стовпця з ідентифікатором; ми не можемо замовити повернутий стовпець; і ми не можемо вибрати два найнижчих записи, використовуючи стандартний SQL, як це можна зробити для двох перших записів.

Ось моя спроба запропонувати рішення:

SELECT * INTO #mytemptable FROM Split('apple,orange,banana,apple,lime',',')
ALTER TABLE #mytemptable ADD tempID INT IDENTITY
SELECT TOP 2 * FROM #mytemptable ORDER BY tempID DESC
DROP TABLE #mytemptable

І ось більш повне рішення:

SELECT * INTO #mytemptable FROM Split('apple,orange,banana,apple,lime',',')
ALTER TABLE #mytemptable ADD tempID INT IDENTITY
DELETE FROM #mytemptable WHERE tempID <= ((SELECT COUNT(*) FROM #mytemptable) - 2)
ALTER TABLE #mytemptable DROP COLUMN tempID
SELECT * FROM #mytemptable
DROP TABLE #mytemptable

Я ні в якому разі не стверджую, що цю ідею слід використовувати в будь-яких обставинах, але вона дає бажані результати.



1

Проблема із замовленням іншого способу полягає в тому, що він часто не використовує індекси. Це також не дуже розширюється, якщо вам коли-небудь потрібно вибрати ряд рядків, які не знаходяться на початку чи в кінці. Альтернативний спосіб полягає в наступному.

DECLARE @NumberOfRows int;
SET @NumberOfRows = (SELECT COUNT(*) FROM TheTable);

SELECT col1, col2,...
FROM (
    SELECT col1, col2,..., ROW_NUMBER() OVER (ORDER BY col1) AS intRow
    FROM TheTable
) AS T
WHERE intRow > @NumberOfRows - 20;

2
1) Якщо зміна напрямку вашого ЗАМОВЛЕННЯ за пунктом "не використовує індекси", тоді отримайте гідну RDBMS! RDBMS ніколи не має байдуже, переміщується він в індексі вперед або назад. 2) Ви стурбовані використанням індексів, але ваше рішення додає послідовність до кожного рядка таблиці ... Це один із способів гарантувати, що відповідний індекс НЕ буде використаний.
Розчарований

1

Відповідь "Том Н" вище є правильною, і це працює для мене в отриманні нижнього 5 рядків.

SELECT [KeyCol1], [KeyCol2], [Col3]
FROM
(SELECT TOP 5 [KeyCol1],
       [KeyCol2],
       [Col3]
  FROM [dbo].[table_name]
  ORDER BY [KeyCol1],[KeyCol2] DESC) SOME_ALAIS
  ORDER BY [KeyCol1],[KeyCol2] ASC

Дякую.


0

спробуйте це.

declare @floor int --this is the offset from the bottom, the number of results to exclude
declare @resultLimit int --the number of results actually retrieved for use
declare @total int --just adds them up, the total number of results fetched initially

--following is for gathering top 60 results total, then getting rid of top 50. We only keep the last 10
set @floor = 50 
set @resultLimit = 10
set @total = @floor + @resultLimit

declare @tmp0 table(
    --table body
)

declare @tmp1 table(
    --table body
)

--this line will drop the wanted results from whatever table we're selecting from
insert into @tmp0
select Top @total --what to select (the where, from, etc)

--using floor, insert the part we don't want into the second tmp table
insert into @tmp1
select top @floor * from @tmp0

--using select except, exclude top x results from the query
select * from @tmp0
except 
select * from @tmp1

що робить ваш код для ОП? Будь ласка, додайте трохи більше пояснень щодо того, як ви намагаєтеся вирішити проблему
techspider

Я зробив правку. Я сподіваюся, що це пояснює це трохи краще, додаючи більше коментарів. Основна ідея - вибрати верхню частину x з таблиці, потім вибрати верхній x - нумеру, а потім використовувати виключно оператор для виключення непотрібних результатів.
HumbleWebDev

0

Я придумав рішення цього питання, яке не вимагає від вас знати кількість повернених рядків.

Наприклад, якщо ви хочете, щоб усі локації входили в таблицю, крім останніх 1 (або 2, або 5, або 34)

SELECT * 
FROM
    (SELECT ROW_NUMBER() OVER (ORDER BY CreatedDate) AS Row, * 
    FROM Locations
    WHERE UserId = 12345) AS SubQuery
WHERE Row > 1 -- or 2, or 5, or 34

0

Запит простого підзапиту, відсортованого за спаданням, з подальшим сортуванням у тому ж стовпці за зростанням робить трюк.

SELECT * FROM 
    (SELECT TOP 200 * FROM [table] t2 ORDER BY t2.[column] DESC) t1
    ORDER BY t1.[column]


0

Спочатку створіть індекс у підзапиті відповідно до початкового порядку таблиці, використовуючи:

ROW_NUMBER () OVER (ORDER BY (SELECT NULL) ) AS RowIndex

Потім упорядкуйте таблицю, що спадає за RowIndexстовпцем, який ви створили в головному запиті:

ORDER BY RowIndex DESC

І, нарешті, використовуйте TOPпотрібну кількість рядків:

    SELECT TOP 1 * --(or 2, or 5, or 34)
    FROM   (SELECT ROW_NUMBER() OVER (ORDER BY  (SELECT NULL) ) AS RowIndex, * 
            FROM MyTable) AS SubQuery
    ORDER BY RowIndex DESC
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.