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

292

Я заинтересован в изучении некоторых (идеально) баз данных агностических способов выбора 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: Фироз Ансари-блог

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

  • 2
    Да. Вот ссылка на информацию о стандарте ISO SQL: troels.arvin.dk/db/rdbms/links/#standards
  • 8
    Просто чтобы указать, что по определению отношения строки в таблице не имеют порядка, поэтому N-ую строку в таблице нельзя выбрать. Что может быть выбрано, так это N-ая строка в наборе строк, возвращаемая (остальной частью) запроса, что и выполняет ваш пример и все остальные ответы. Для большинства это может быть просто семантикой, но это указывает на основную проблему вопроса. Если вам нужно вернуть OrderNo N , введите столбец OrderSequenceNo в таблицу и сгенерируйте его из независимого генератора последовательностей при создании нового заказа.
Показать ещё 1 комментарий
Теги:
database

27 ответов

262
Лучший ответ

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

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

  • 2
    MySQL также использует синтаксис OFFSET и LIMIT. Firebird использует ключевые слова FIRST и SKIP, но они размещаются сразу после SELECT.
  • 4
    WHERE rownumber = n не должно быть WHERE rownumber = n чтобы получить только n- WHERE rownumber = n строку?
65

Синтаксис LIMIT/OFFSET в PostgreSQL:

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

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

По-видимому, стандарт SQL молчал о проблеме с ограничениями вне сумасшедших функций окон, поэтому каждый реализует ее по-разному.

  • 0
    Также работает в MySQL
22

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

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

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

  • 0
    Это выглядит как лучший способ ограничить запрос встроенным значением смещения. Но разве мы не должны использовать 0,14 здесь? 1,15 выйдет из первого ряда.
  • 0
    Что означает 15, хотя? Я знаю, что 1 говорит, чтобы получить одну запись. Запятая не используется в примере, который я проверил 1keydata.com/sql/sql-limit.html
Показать ещё 2 комментария
16

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

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

Это получит пятый элемент, измените второй верхний номер, чтобы получить другой n-й элемент

Только SQL-сервер (я думаю), но должен работать над более старыми версиями, которые не поддерживают ROW_NUMBER().

  • 0
    Я собираюсь использовать это, поскольку ROW_NUMBER () не работает в SQL 2000 (да, у нас все еще есть клиент на SQL 2000) В частности, я собираюсь заменить '5' на переменную цикла итератора и использовать что копировать и модифицировать каждую строку таблицы по очереди. Может быть, кто-то увидит этот комментарий и сочтет это полезным
15

SQL 2005 и выше имеет встроенную функцию. Используйте функцию ROW_NUMBER(). Он отлично подходит для веб-страниц с < Пред. И Следующий → стиль просмотра:

Синтаксис:

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

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

select *
from thetable
limit n-1, 1
  • 0
    какие технологии?
9

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

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

Это даст вам 10-ю строку таблицы emp!

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

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

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

И в SQL: 2008 был добавлен несколько более простой подход, используя
OFFSET skip ROWS FETCH FIRST n ROWS ONLY

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

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

СЕРВЕР SQL


Выберите 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
5

Когда мы работали в MSSQL 2000, мы сделали то, что мы назвали "тройной флип":

EDITED

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

Это было не изящно, и это было не быстро, но это сработало.

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

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

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

Здесь вы можете получить последнюю строку, заполняя N = 0, второе последним - N = 1, четвертое последнее заполнение N = 3 и т.д.

Это очень распространенный вопрос по поводу интервью, и это очень простые его аспекты.

Далее Если вы хотите, чтобы сумма, идентификатор или некоторый числовой порядок сортировки превышали u, вы можете использовать функцию CAST в MySQL.

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

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

2

Например, если вы хотите выбрать каждую 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 на любой номер, который вы хотите.

2

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

2

ADD:

LIMIT n,1

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

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

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

  • 1
    За исключением Oracle, DB2 - это почти каждая база данных корпоративного уровня во всем мире. PostgreSQL - это единственная корпоративная база данных, которая поддерживает ключевое слово LIMIT, и это главным образом потому, что, будучи открытым исходным кодом, он должен быть доступен для игнорируемой ACID толпы MySQL.
  • 3
    @AlexD Этот «ответ» был опубликован еще в старые времена Stackoverflow до того, как комментарии были реализованы. Я бы опубликовал это как комментарий к другому ответу, но в то время комментариев не было.
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
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-й записи из таблицы Employee, ваш запрос должен быть

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

Для SQL Server общий путь по номеру строки таков: SET ROWCOUNT @row - @row = номер строки, с которой вы хотите работать.

Пример:

set rowcount 20 - устанавливает строку до 20-й строки

выберите мясо, сыр из dbo.sandwich - выберите столбцы из таблицы в 20-й строке

set rowcount 0 --sets rowcount обратно во все строки

Это вернет информацию о 20-й строке. Обязательно введите значение 0 после.

Я знаю noobish, но я SQL noob, и я использовал его так, что я могу сказать?

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

В Sybase SQL Anywhere:

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

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

0

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

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

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

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

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

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

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

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню