Як вибрати 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.