Як вибрати n-й рядок у таблиці бази даних SQL?


399

Мені цікаво вивчити деякі (в ідеалі) бази даних агностики щодо вибору n- го рядка з таблиці бази даних. Також було б цікаво подивитися, як цього можна досягти, використовуючи нативну функціональність наступних баз даних:

  • SQL Server
  • MySQL
  • PostgreSQL
  • SQLite
  • Oracle

В даний час я роблю щось подібне до SQL Server 2005, але мені було б цікаво ознайомитися з іншими більш агностичними підходами:

WITH Ordered AS (
SELECT ROW_NUMBER() OVER (ORDER BY OrderID) AS RowNumber, OrderID, OrderDate
FROM Orders)
SELECT *
FROM Ordered
WHERE RowNumber = 1000000

Заслуга вищезгаданого SQL: Веб-журнал Firoz Ansari

Оновлення: Дивіться відповідь тролів Арвіна щодо стандарту SQL. Тролі, чи є у вас посилання, які ми можемо навести?


3
Так. Ось посилання на інформацію про стандарт ISO SQL: troels.arvin.dk/db/rdbms/links/#standards
Арвін

13
Просто зазначимо, що за визначенням відношення рядки в таблиці не мають порядку, тому N-й рядок у таблиці не може бути обраний. Що можна вибрати, це N-й рядок у наборі рядків, що повертається (рештою) запитом. Це є вашим прикладом та всіма іншими відповідями. Для більшості це може бути просто семантика, але це вказує на основну проблему питання. Якщо вам потрібно повернутися OrderNo N, то введіть у таблицю стовпчик OrderSequenceNo та створіть його з незалежного генератора послідовностей після створення нового замовлення.
Дамір Сударевич

2
Стандарт SQL визначає параметр offset x fetch first y rows only. На даний момент підтримується (принаймні) Postgres, Oracle12, DB2.
a_horse_with_no_name

Відповіді:


349

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

Дійсно хороший веб-сайт, який розповідає про це та інші речі, - http://troels.arvin.dk/db/rdbms/#select-limit .

В основному, PostgreSQL і MySQL підтримують нестандартні:

SELECT...
LIMIT y OFFSET x 

Oracle, DB2 та MSSQL підтримують стандартні функції вікна:

SELECT * FROM (
  SELECT
    ROW_NUMBER() OVER (ORDER BY key ASC) AS rownumber,
    columns
  FROM tablename
) AS foo
WHERE rownumber <= n

(який я щойно скопіював із сайту, пов’язаного вище, оскільки я ніколи не використовую ці БД)

Оновлення: Станом на PostgreSQL 8.4 підтримуються стандартні функції вікон, тому очікуйте, що другий приклад також працює для PostgreSQL.

Оновлення: підтримка функцій вікон SQLite додана у версії 3.25.0 2018-09-15, тому обидві форми також працюють у SQLite.


3
MySQL також використовує синтаксис OFFSET та LIMIT. Firebird використовує ключові слова FIRST та SKIP, але вони розміщуються відразу після SELECT.
Дуг

7
Чи не повинно це бути лише WHERE rownumber = nдля отримання n-го ряду?
Стів Беннетт

MySQL підтримує віконні функції з версії 8. MariaDB з версії 10.2
Пол Шпігель

101

PostgreSQL підтримує функції вікон , визначені стандартом SQL, але вони незручні, тому більшість людей використовує (нестандартне) LIMIT/OFFSET :

SELECT
    *
FROM
    mytable
ORDER BY
    somefield
LIMIT 1 OFFSET 20;

Цей приклад вибирає 21-й ряд. OFFSET 20повідомляє Postgres пропустити перші 20 записів. Якщо ви не вказали ORDER BYпункт, немає гарантії, який запис ви отримаєте, що рідко буває корисним.


31

Я не впевнений ні в чому іншому, але я знаю, що в SQLite та MySQL немає впорядкування рядків за замовчуванням. Принаймні, у цих двох діалектах наступний фрагмент захоплює 15-й запис із таблиці_, сортуючи за датою / часом додавання:

SELECT * FROM the_table ORDER BY added DESC LIMIT 1,15

(звичайно, вам потрібно буде додати поле DATETIME і встановити його за датою / часом, коли запис було додано ...)


Це виглядає як найкращий спосіб обмежити запит вбудованим значенням зміщення. Але чи не слід тут використовувати 0,14? 1,15 залишить перший ряд.
Гладіатор

Що означає 15, хоча? Я знаю, що 1 говорить, щоб отримати один запис. Кома не використовується в прикладі , який я перевірив 1keydata.com/sql/sql-limit.html
committedandroider

1
На насправді, тут php.about.com/od/mysqlcommands/g/Limit_sql.htm , якщо ви хочете , щоб захопити запис п'ятнадцятого б не зробити LIMIT 14, 1 (0th є першим елементом, 1 довжини
committedandroider

ВИБІРТИ * ВІД ВИДАЛЕНОГО ЗАМОВЛЕННЯ ДО доданого DESC LIMIT 15,1
JerryGoyal

25

Ця SQL 2005 і вище має цю вбудовану функцію. Використовуйте функцію ROW_NUMBER (). Він відмінно підходить для веб-сторінок із переглядом стилів << Попередня та Наступна >>

Синтаксис:

SELECT
    *
FROM
    (
        SELECT
            ROW_NUMBER () OVER (ORDER BY MyColumnToOrderBy) AS RowNum,
            *
        FROM
            Table_1
    ) sub
WHERE
    RowNum = 23

Я віддаю перевагу такому рішенню, оскільки він відчуває себе більш прямо.
FoxArc

18

Я підозрюю, що це дико неефективно, але це досить простий підхід, який працював над невеликим набором даних, на якому я його спробував.

select top 1 field
from table
where field in (select top 5 field from table order by field asc)
order by field desc

Це отримає 5-й елемент, змінити другий верхній номер, щоб отримати інший n-й елемент

Лише SQL-сервер (я думаю), але повинен працювати на старих версіях, які не підтримують ROW_NUMBER ().


Я буду використовувати це, оскільки ROW_NUMBER () не працює в SQL 2000 (так, у нас ще є клієнт на SQL 2000). Зокрема, я заміню "5" змінною ітератора циклу і використовую що по черзі копіювати та змінювати кожен рядок таблиці. Можливо, хтось побачить цей коментар і знайде це корисним
Inversus


14

Перевірте це на SQL Server:

Select top 10 * From emp 
EXCEPT
Select top 9 * From emp

Це дасть вам 10-й рядок таблиці emp!


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

11

На противагу тому, що деякі відповіді стверджують, стандарт SQL не мовчить з цього приводу.

Починаючи з SQL: 2003, ви можете використовувати "віконні функції" для пропуску рядків та обмеження наборів результатів.

І в SQL: 2008 був доданий трохи простіший підхід, який використовує
OFFSET skip ROWS FETCH FIRST n ROWS ONLY

Особисто я не вважаю, що доповнення SQL: 2008 було дуже потрібне, тому якби я був ISO, я б утримав його від уже досить великого стандарту.


Приємно, що є стандарт, однак, людям подобається життя мені легше, і так приємно
мікрософт

7

Коли ми працювали в MSSQL 2000, ми зробили те, що ми назвали "потрійним переворотом":

ВИДАЛЕНО

DECLARE @InnerPageSize int
DECLARE @OuterPageSize int
DECLARE @Count int

SELECT @Count = COUNT(<column>) FROM <TABLE>
SET @InnerPageSize = @PageNum * @PageSize
SET @OuterPageSize = @Count - ((@PageNum - 1) * @PageSize)

IF (@OuterPageSize < 0)
    SET @OuterPageSize = 0
ELSE IF (@OuterPageSize > @PageSize)
    SET @OuterPageSize = @PageSize

DECLARE @sql NVARCHAR(8000)

SET @sql = 'SELECT * FROM
(
    SELECT TOP ' + CAST(@OuterPageSize AS nvarchar(5)) + ' * FROM
    (
        SELECT TOP ' + CAST(@InnerPageSize AS nvarchar(5)) + ' * FROM <TABLE> ORDER BY <column> ASC
    ) AS t1 ORDER BY <column> DESC
) AS t2 ORDER BY <column> ASC'

PRINT @sql
EXECUTE sp_executesql @sql

Це було не елегантно, і не було швидко, але це спрацювало.


Скажімо, у вас є 25 рядків, і ви хочете, щоб третя сторінка була розміщена з 10 сторінок, тобто рядків 21-25. Найпотаємніший запит отримує верхні 30 рядків (рядки 1-25). Середній запит отримує останні 10 рядків (рядки 25-16). Зовнішній запит переробляє їх і повертає рядки 16-25. Це явно неправильно, якщо ви хотіли рядки 21-25.
Білл Карвін

Тепер це не працює, якщо ми хочемо середню сторінку. Скажімо, у нас є 25 рядків, і ми хочемо другу сторінку, тобто рядки 11-20. Внутрішній запит отримує верхній 2 * 10 = 20 рядків, або рядки 1-20. Середній запит отримує останні 15 рядків: 25 - ((2-1) * 10) = 15, що дає рядки 20-6. Останній запит скасовує порядок і повертає рядки 6-20. Ця методика не працює, якщо загальна кількість рядків не кратна бажаному розміру сторінки.
Білл Карвін

Мабуть, найкращим висновком є ​​те, що нам слід оновити будь-які інстанції MS SQL Server 2000. :-) Зараз майже 2012 рік, і ця проблема вже багато років вирішується кращими способами!
Білл Карвін

@Bill Karwin: Зверніть увагу на IF / ELSE IFблоки нижче OuterPageSizeобчислення - на сторінках 1 і 2 вони зменшать OuterPageSizeзначення назад до 10. На сторінці 3 (рядки 21-25) обчислення вірно поверне 5, а на всіх сторінках 4 і більше, негативний результат обчислення буде замінено на 0 (хоча, мабуть, просто швидше повернути порожній рядок даних у цей момент).
Адам V

О, я бачу зараз. Ну, я притримуюсь своєї думки, що сьогодні використовувати MS SQL Server 2000 не варто.
Білл Карвін

6

SQL SERVER


Виберіть n-й запис зверху

SELECT * FROM (
SELECT 
ID, NAME, ROW_NUMBER() OVER(ORDER BY ID) AS ROW
FROM TABLE 
) AS TMP 
WHERE ROW = n

виберіть n-й запис знизу

SELECT * FROM (
SELECT 
ID, NAME, ROW_NUMBER() OVER(ORDER BY ID DESC) AS ROW
FROM TABLE 
) AS TMP 
WHERE ROW = n

5

Oracle:

select * from (select foo from bar order by foo) where ROWNUM = x

1
where ROWNUM = xбуде працювати лише для x = 1 у БД Oracle. тобто where ROWNUM = 2не поверне жодних рядків.
ствердну

5

У Oracle 12c ви можете використовувати OFFSET..FETCH..ROWS опцію зORDER BY

Наприклад, щоб отримати 3-й запис зверху:

SELECT * 
FROM   sometable
ORDER BY column_name
OFFSET 2 ROWS FETCH NEXT 1 ROWS ONLY;

4

Ось швидке рішення вашої плутанини.

SELECT * FROM table ORDER BY `id` DESC LIMIT N, 1

Тут ви можете отримати Останній рядок шляхом заповнення N = 0, другий останній за N = 1, Четвертий останній за заповненням N = 3 тощо.

Це дуже поширене питання в інтерв'ю, і це дуже прості відповіді.

Далі Якщо ви хочете суму, ідентифікатор чи якийсь порядок чисельного сортування, ви можете перейти до функції CAST у MySQL.

SELECT DISTINCT (`amount`) FROM cart ORDER BY CAST( `amount` AS SIGNED ) DESC LIMIT 4 , 1

Тут заповнивши N = 4, ви зможете отримати П’ятий останній рекорд найбільшої суми з таблиці CART. Ви можете відповідати назві поля та таблиці та придумати рішення.



3

Наприклад, якщо ви хочете вибрати кожен 10-й рядок в MSSQL, ви можете використовувати;

SELECT * FROM (
  SELECT
    ROW_NUMBER() OVER (ORDER BY ColumnName1 ASC) AS rownumber, ColumnName1, ColumnName2
  FROM TableName
) AS foo
WHERE rownumber % 10 = 0

Просто візьміть MOD і змініть номер 10 тут будь-яке число, яке вам потрібно.


3

Для SQL Server загальний спосіб перейти за номером рядків такий:

SET ROWCOUNT @row --@row = the row number you wish to work on.

Наприклад:

set rowcount 20   --sets row to 20th row

select meat, cheese from dbo.sandwich --select columns from table at 20th row

set rowcount 0   --sets rowcount back to all rows

Це поверне інформацію про 20-й ряд. Обов’язково введіть у рядок 0 згодом.


2

Обмеження n, 1 не працює в MS SQL Server. Я думаю, що мова йде лише про єдину основну базу даних, яка не підтримує цей синтаксис. Чесно кажучи, він не є частиною стандарту SQL, хоча настільки широко підтримується, що і повинен бути. У всьому, крім SQL-сервера LIMIT працює чудово. Для сервера SQL я не зміг знайти елегантне рішення.


1
За винятком Oracle, DB2, також майже про кожну базу даних підприємств у всьому світі. PostgreSQL - це єдина база даних, що підтримує підприємство, яка підтримує ключове слово LIMIT, і це, головним чином, тому, що, щоб бути відкритим кодом, вона повинна бути доступною натовпу ігноруючих MySQL ACID.
Девід

3
@AlexD Ця "відповідь" була розміщена ще в старі часи Stackoverflow до того, як коментарі були втілені. Я б опублікував це як коментар до іншої відповіді, але в той час коментарів не було.
Kibbee

2

Ось загальна версія проростка, яку я нещодавно писав для Oracle, що дозволяє динамічно робити підкачки / сортування - HTH

-- p_LowerBound = first row # in the returned set; if second page of 10 rows,
--                this would be 11 (-1 for unbounded/not set)
-- p_UpperBound = last row # in the returned set; if second page of 10 rows,
--                this would be 20 (-1 for unbounded/not set)

OPEN o_Cursor FOR
SELECT * FROM (
SELECT
    Column1,
    Column2
    rownum AS rn
FROM
(
    SELECT
        tbl.Column1,
        tbl.column2
    FROM MyTable tbl
    WHERE
        tbl.Column1 = p_PKParam OR
        tbl.Column1 = -1
    ORDER BY
        DECODE(p_sortOrder, 'A', DECODE(p_sortColumn, 1, Column1, 'X'),'X'),
        DECODE(p_sortOrder, 'D', DECODE(p_sortColumn, 1, Column1, 'X'),'X') DESC,
        DECODE(p_sortOrder, 'A', DECODE(p_sortColumn, 2, Column2, sysdate),sysdate),
        DECODE(p_sortOrder, 'D', DECODE(p_sortColumn, 2, Column2, sysdate),sysdate) DESC
))
WHERE
    (rn >= p_lowerBound OR p_lowerBound = -1) AND
    (rn <= p_upperBound OR p_upperBound = -1);

2

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


2

Для сервера SQL наступний поверне перший рядок із таблиці подання.

declare @rowNumber int = 1;
    select TOP(@rowNumber) * from [dbo].[someTable];
EXCEPT
    select TOP(@rowNumber - 1) * from [dbo].[someTable];

Ви можете переглядати значення за допомогою такого типу:

WHILE @constVar > 0
BEGIN
    declare @rowNumber int = @consVar;
       select TOP(@rowNumber) * from [dbo].[someTable];
    EXCEPT
       select TOP(@rowNumber - 1) * from [dbo].[someTable];  

       SET @constVar = @constVar - 1;    
END;

1

У Sybase SQL Anywhere:

SELECT TOP 1 START AT n * from table ORDER BY whatever

Не забувайте ЗАМОВИТИ або це безглуздо.


1

T-SQL - Вибір N'th RecordNumber з таблиці

select * from
 (select row_number() over (order by Rand() desc) as Rno,* from TableName) T where T.Rno = RecordNumber

Where  RecordNumber --> Record Number to Select
       TableName --> To be Replaced with your Table Name

Наприклад, наприклад, для вибору 5-ї записи з таблиці Співробітник, ваш запит повинен бути

select * from
 (select row_number() over (order by Rand() desc) as Rno,* from Employee) T where T.Rno = 5


1
SELECT
    top 1 *
FROM
    table_name
WHERE
    column_name IN (
        SELECT
            top N column_name
        FROM
            TABLE
        ORDER BY
            column_name
    )
ORDER BY
    column_name DESC

Я написав цей запит на пошук N-го рядка. Приклад із цим запитом буде

SELECT
    top 1 *
FROM
    Employee
WHERE
    emp_id IN (
        SELECT
            top 7 emp_id
        FROM
            Employee
        ORDER BY
            emp_id
    )
ORDER BY
    emp_id DESC

0

неймовірно, що ви можете знайти SQL-движок, що виконує цей ...

WITH sentence AS
(SELECT 
    stuff,
    row = ROW_NUMBER() OVER (ORDER BY Id)
FROM 
    SentenceType
    )
SELECT
    sen.stuff
FROM sentence sen
WHERE sen.row = (ABS(CHECKSUM(NEWID())) % 100) + 1

0

Нічого фантазії, ніяких спеціальних функцій, якщо ви використовуєте Caché, як я ...

SELECT TOP 1 * FROM (
  SELECT TOP n * FROM <table>
  ORDER BY ID Desc
)
ORDER BY ID ASC

З огляду на те, що у вас є стовпець ідентифікатора або стовпець дати, якому можна довіряти.


0

Ось як я це зробив би в DB2 SQL, я вважаю, що RRN (відносний номер запису) зберігається в таблиці через O / S;

SELECT * FROM (                        
   SELECT RRN(FOO) AS RRN, FOO.*
   FROM FOO                         
   ORDER BY RRN(FOO)) BAR             
 WHERE BAR.RRN = recordnumber

0
select * from 
(select * from ordered order by order_id limit 100) x order by 
x.order_id desc limit 1;

Спочатку виберіть топ-100 рядків, упорядкувавшись у порядку зростання, а потім виберіть останній рядок, упорядкувавшись у порядку зменшення та обмеживши 1. Однак це дуже дороге твердження, оскільки воно отримує доступ до даних двічі.


0

Мені здається, що для ефективності вам потрібно 1) генерувати випадкове число між 0 і одним меншим, ніж кількість записів бази даних, і 2) вміти вибирати рядок у цій позиції. На жаль, різні бази даних мають різні генератори випадкових чисел і різні способи вибору рядка в позиції в наборі результатів - зазвичай ви вказуєте, скільки рядків пропустити і скільки рядків ви хочете, але це робиться по-різному для різних баз даних. Ось щось працює для мене в SQLite:

select * 
from Table 
limit abs(random()) % (select count(*) from Words), 1;

Це залежить від того, чи зможете використовувати підзапит у обмеженому пункті (який у SQLite є LIMIT <recs to skip>, <recs to take>) Вибір кількості записів у таблиці повинен бути особливо ефективним, будучи частиною бази даних метадані, але це залежить від реалізації бази даних. Крім того, я не знаю, чи запит насправді створить набір результатів перед тим, як отримати N-й запис, але я би сподівався, що цього не потрібно. Зауважте, що я не вказую пункт "замовлення за". Можливо, краще "замовити" щось на кшталт первинного ключа, який матиме індекс - отримання N-го запису з індексу може бути швидшим, якщо база даних не зможе отримати N-й запис із самої бази даних, не будуючи набір результатів .


0

Найбільш підходящу відповідь, яку я бачив у цій статті для sql-сервера

WITH myTableWithRows AS (
    SELECT (ROW_NUMBER() OVER (ORDER BY myTable.SomeField)) as row,*
    FROM myTable)
SELECT * FROM myTableWithRows WHERE row = 3
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.