Как выбрать n-ую строку в таблице базы данных SQL?


400

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

  • SQL Server
  • MySQL
  • PostgreSQL
  • SQLite
  • оракул

В настоящее время я делаю что-то вроде следующего в 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: блог Фироза Ансари

Обновление: см . Ответ Troels Arvin относительно стандарта SQL. Troels, есть ли у вас ссылки, которые мы можем цитировать?


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

Ответы:


350

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

Действительно хороший сайт, который рассказывает об этом и других вещах, это 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
Пол Шпигель

102

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 , если вы хотите получить 15- ую запись, вы не выполните LIMIT 14, 1 (0-й - первый элемент, 1 длины
commitandroider

это будет ВЫБРАТЬ * ИЗ the_table ORDER BY добавлен 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, я бы не использовал его в уже достаточно большом стандарте.


Приятно, что есть стандарт, который облегчает жизнь людям, таким как я, и так приятно от Microsoft делать вещи стандартным образом :)
user230910

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 год, и эта проблема была решена в течение многих лет лучше!
Билл Карвин

@ Билл Карвин: Обратите внимание на 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 12c Вы можете использовать OFFSET..FETCH..ROWS опцию сORDER BY

Например, чтобы получить третью запись сверху:

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 и так далее.

Это очень распространенный вопрос во время интервью, и это очень просто.

Далее Если вы хотите Amount, ID или какой-либо Числовой Порядок сортировки, вы можете перейти к функции CAST в MySQL.

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

Здесь, заполнив N = 4, вы сможете получить пятую последнюю запись наибольшей суммы из таблицы CART. Вы можете указать свое поле и имя таблицы и найти решение.



3

Например, если вы хотите выбрать каждую десятую строку в MSSQL, вы можете использовать;

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

Просто возьмите мод и измените номер 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

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


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

3
@AlexD Этот «ответ» был опубликован еще в старые времена Stackoverflow до того, как были реализованы комментарии. Я бы опубликовал это как комментарий к другому ответу, но в то время комментариев не было.
Кибби

2

Вот общая версия sproc, которую я недавно написал для 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

Не забывайте ORDER BY или это бессмысленно.


1

T-SQL - выбор N-го номера записи из таблицы

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-ю запись из таблицы Employee, ваш запрос должен быть

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

Учитывая, что у вас есть столбец ID или столбец с датой, которому можно доверять.


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;

Это зависит от возможности использовать подзапрос в предложении limit (в SQLite это LIMIT <recs to skip>, <recs to take>). Выбор количества записей в таблице должен быть особенно эффективным, поскольку он является частью базы данных. метаданные, но это зависит от реализации базы данных. Кроме того, я не знаю, будет ли запрос фактически формировать набор результатов перед извлечением N-й записи, но я надеюсь, что в этом нет необходимости. Обратите внимание, что я не указываю предложение "order by". Возможно, было бы лучше «упорядочить» что-то вроде первичного ключа, который будет иметь индекс - получение 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.