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

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

  • 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: Блог Фироза Ансари

Обновлять: См. Ответ Троэлса Арвина относительно стандарта SQL. Троэлс, есть ли у вас ссылки, которые мы можем процитировать?

Да. Вот ссылка на информацию о стандарте ISO SQL: troels.arvin.dk/db/rdbms/links/#standards

Troels Arvin 31.10.2008 10:10

Просто чтобы указать, что по определению отношения строки в таблице не имеют порядка, поэтому N-я строка в таблице не может быть выбрана. Что может быть выбрано, так это N-я строка в наборе строк, возвращаемом (остальной частью) запросом, что и выполняет ваш пример и все другие ответы. Для большинства это может быть просто семантикой, но это указывает на основную проблему вопроса. Если вам действительно нужно вернуть OrderNo N, введите в таблицу столбец OrderSequenceNo и сгенерируйте его из генератор независимой последовательности при создании нового заказа.

Damir Sudarevic 26.10.2011 17:21

Стандарт SQL определяет опцию offset x fetch first y rows only. В настоящее время поддерживается (по крайней мере) Postgres, Oracle12, DB2.

a_horse_with_no_name 04.06.2016 16:28
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
В последние годы архитектура микросервисов приобрела популярность как способ построения масштабируемых и гибких приложений. Laravel , популярный PHP...
Как построить CRUD-приложение в Laravel
Как построить CRUD-приложение в Laravel
Laravel - это популярный PHP-фреймворк, который позволяет быстро и легко создавать веб-приложения. Одной из наиболее распространенных задач в...
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
В предыдущем посте мы создали функциональность вставки и чтения для нашей динамической СУБД. В этом посте мы собираемся реализовать функции обновления...
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
Роли и разрешения пользователей без пакета Laravel 9
Роли и разрешения пользователей без пакета Laravel 9
Этот пост изначально был опубликован на techsolutionstuff.com .
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
В предыдущей статье мы завершили установку базы данных, для тех, кто не знает.
424
3
829 842
33
Перейти к ответу Данный вопрос помечен как решенный

Ответы 33

ДОБАВИТЬ:

LIMIT n,1

Это ограничит результаты одним результатом, начиная с результата n.

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

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

David 06.03.2009 16:39

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

Kibbee 28.08.2012 20:33

Вот общая версия 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);

Я не уверен в остальном, но я знаю, что SQLite и MySQL не имеют «стандартного» порядка строк. По крайней мере, для этих двух диалектов следующий фрагмент берет 15-ю запись из the_table, сортируя ее по дате / времени добавления:

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

(конечно, вам нужно будет добавить поле DATETIME и установить его на дату / время, когда была добавлена ​​запись ...)

Похоже, это лучший способ ограничить запрос встроенным значением смещения. Но разве мы не должны использовать здесь 0,14? 1,15 выйдет из первого ряда.

Gladiator 23.07.2014 17:11

Но что означает 15? Я знаю, что 1 говорит, что нужно получить одну пластинку. Запятая не используется в примере, который я проверял 1keydata.com/sql/sql-limit.html

committedandroider 06.02.2015 09:25

Фактически, отсюда php.about.com/od/mysqlcommands/g/Limit_sql.htm, если вы хотите получить 15-ю запись, разве вы не сделали бы LIMIT 14, 1 (0-й - первый элемент, 1 длины

committedandroider 06.02.2015 09:27

это должно быть SELECT * FROM the_table ORDER BY добавлено DESC LIMIT 15,1

GorvGoyl 27.04.2016 09:28

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

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 18.05.2013 00:02

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

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

В этом примере выбирается 21-я строка. OFFSET 20 говорит Postgres пропустить первые 20 записей. Если вы не укажете предложение ORDER BY, нет гарантии, какую запись вы получите обратно, что редко бывает полезным.

Когда мы работали с 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.

Bill Karwin 30.12.2011 12:53

Теперь это не работает, если нам нужна средняя страница. Скажем, у нас есть 25 строк, и нам нужна вторая страница, то есть строки 11-20. Внутренний запрос получает верхние 2 * 10 = 20 строк или строки 1-20. Средний запрос получает последние 15 строк: 25 - ((2-1) * 10) = 15, что дает строки 20-6. Последний запрос меняет порядок и возвращает строки 6–20. Этот метод не работает, если общее количество строк не кратно желаемому размеру страницы.

Bill Karwin 30.12.2011 21:14

Возможно, лучший вывод состоит в том, что мы должны обновить все оставшиеся экземпляры MS SQL Server 2000. :-) Уже почти 2012 год, и уже много лет эта проблема решается лучше!

Bill Karwin 30.12.2011 21:15

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

Adam V 01.01.2012 05:50

О, теперь я понимаю. Что ж, я придерживаюсь своего мнения, что использование MS SQL Server 2000 сегодня не стоит проблем.

Bill Karwin 01.01.2012 06:57

Оракул:

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

where ROWNUM = x будет работать только для x = 1 в Oracle DB. то есть where ROWNUM = 2 не вернет никаких строк.

aff 11.07.2019 04:18

Но на самом деле, разве все это не просто салонные уловки для хорошего дизайна базы данных? Несколько раз мне требовались такие функции, как этот простой одноразовый запрос для быстрого создания отчета. В любой реальной работе использование подобных приемов создает проблемы. Если требуется выбрать конкретную строку, просто создайте столбец с последовательным значением и покончите с этим.

В Sybase SQL Anywhere:

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

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

Ответ принят как подходящий

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

Действительно хороший сайт, рассказывающий об этом и других вещах, - 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 от 15 сентября 2018 г., поэтому обе формы также работают в SQLite.

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

Doug 01.12.2011 21:03

Разве это не должно быть WHERE rownumber = n, чтобы получить только n-ю строку?

Steve Bennett 12.09.2017 10:14

MySQL поддерживает оконные функции с версии 8. MariaDB с версии 10.2.

Paul Spiegel 25.07.2019 23:01

1 небольшое изменение: n-1 вместо n.

select *
from thetable
limit n-1, 1

какая технология?

user230910 15.01.2018 04:21

Вопреки утверждениям некоторых ответов, стандарт SQL не молчит по этому поводу.

Начиная с SQL: 2003, вы можете использовать «оконные функции», чтобы пропускать строки и ограничивать наборы результатов.

А в SQL: 2008 был добавлен немного более простой подход с использованием
OFFSET skip ROWS FETCH FIRST n ROWS ONLY
.

Лично я не думаю, что добавление SQL: 2008 было действительно необходимо, поэтому, если бы я был ISO, я бы не включил его в уже довольно крупный стандарт.

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

user230910 15.01.2018 05:08

невероятно, что вы можете найти 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

SELECT * FROM emp a
WHERE  n = (SELECT COUNT( _rowid)
              FROM emp b
             WHERE a. _rowid >= b. _rowid);

Для 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-й строке. Не забудьте после этого ввести rowcount 0.

В SQL 2005 и более поздних версиях эта функция встроена. Используйте функцию ROW_NUMBER (). Он отлично подходит для веб-страниц с> стилем просмотра:

Синтаксис:

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

Я предпочитаю это решение, так как оно кажется более простым.

FoxArc 04.12.2019 18:37

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

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

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

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

Просто возьмите МОД и измените число 10 здесь на любое число, которое хотите.

Вот быстрое решение вашей проблемы.

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. Вы можете подобрать свое поле и имя таблицы и придумать решение.

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

Проверьте это на SQL Server:

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

Это даст вам 10-й РЯД таблицы emp!

Вы уже ответили на этот вопрос. здесь Удалите тот ответ, который считаете неправильным. Если вы считаете, что оба ответа верны, разместите оба ответа в одном месте.

SpringLearner 16.10.2014 15:04

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

Ничего особенного, никаких специальных функций, если вы используете Caché, как я ...

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

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

Вот как я сделал бы это в DB2 SQL, я считаю, что RRN (относительный номер записи) хранится в таблице оператором / оператором;

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

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

Сначала выберите 100 первых строк в порядке возрастания, а затем выберите последнюю строку в порядке убывания и ограничьте до 1. Однако это очень дорогостоящий оператор, поскольку он обращается к данным дважды.

Мне кажется, что для эффективности вам нужно 1) сгенерировать случайное число от 0 до одного меньше, чем количество записей в базе данных, и 2) иметь возможность выбрать строку в этой позиции. К сожалению, разные базы данных имеют разные генераторы случайных чисел и разные способы выбора строки в позиции в наборе результатов - обычно вы указываете, сколько строк пропустить и сколько строк вы хотите, но для разных баз данных это делается по-разному. Вот что у меня работает в SQLite:

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

Это зависит от возможности использовать подзапрос в предложении limit (который в SQLite равен LIMIT , ) Выбор количества записей в таблице должен быть особенно эффективным, поскольку он является частью метаданных базы данных, но это зависит от реализации базы данных. Кроме того, я не знаю, действительно ли запрос построит набор результатов перед извлечением N-й записи, но я надеюсь, что в этом нет необходимости. Обратите внимание, что я не указываю предложение «порядок по». Возможно, лучше "упорядочить" что-то вроде первичного ключа, который будет иметь индекс - получение N-й записи из индекса может быть быстрее, если база данных не может получить N-ю запись из самой базы данных без построения набора результатов. .

В Oracle 12c вы можете использовать опцию OFFSET..FETCH..ROWS с ORDER BY

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

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

Для 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;

Наиболее подходящий ответ, который я видел в статье это для sql server

WITH myTableWithRows AS (
    SELECT (ROW_NUMBER() OVER (ORDER BY myTable.SomeField)) as row,*
    FROM myTable)
SELECT * FROM myTableWithRows WHERE row = 3

Если вы хотите посмотреть на собственные функции: MySQL, PostgreSQL, SQLite и Oracle (в основном SQL Server, похоже, не имеет этой функции) вы можете ДЕЙСТВИТЕЛЬНО использовать оконную функцию NTH_VALUE. Источник Oracle: Функции Oracle: NTH_VALUE

Я действительно экспериментировал с этим в нашей базе данных Oracle, чтобы провести некоторое сравнение первой строки (после заказа) со второй строкой (опять же, после заказа). Код будет выглядеть примерно так (если вы не хотите переходить по ссылке):

SELECT DISTINCT dept_id
     , NTH_VALUE(salary,2) OVER (PARTITION BY dept_id ORDER BY salary DESC
           RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) 
        AS "SECOND HIGHEST"
     , NTH_VALUE(salary,3) OVER (PARTITION BY dept_id ORDER BY salary DESC
           RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
        AS "THIRD HIGHEST"
  FROM employees
 WHERE dept_id in (10,20)
 ORDER 
    BY dept_id;

Мне это показалось довольно интересным, и я хотел бы, чтобы они разрешили мне его использовать.

Я немного опоздал на вечеринку, но я сделал это без необходимости использования окон или использования

WHERE x IN (...)
SELECT TOP 1
--select the value needed from t1
[col2]
FROM
(
   SELECT TOP 2 --the Nth row, alter this to taste
   UE2.[col1],
   UE2.[col2],
   UE2.[date],
   UE2.[time],
   UE2.[UID]
   FROM
   [table1] AS UE2
   WHERE
   UE2.[col1] = ID --this is a subquery 
   AND
   UE2.[col2] IS NOT NULL
   ORDER BY
   UE2.[date] DESC, UE2.[time] DESC --sorting by date and time newest first
) AS t1
ORDER BY t1.[date] ASC, t1.[time] ASC --this reverses the order of the sort in t1

Кажется, работает довольно быстро, хотя, честно говоря, у меня всего около 500 строк данных.

Это работает в MSSQL

WITH r AS (
  SELECT TOP 1000 * FROM emp
)
SELECT * FROM r
EXCEPT
SELECT TOP 999 FROM r

Это даст 1000-ю строку в SQL Server.

Другие вопросы по теме