如何选择 SQL 数据库表中的第 n 行?

Posted

技术标签:

【中文标题】如何选择 SQL 数据库表中的第 n 行?【英文标题】:How to select the nth row in a SQL database table? 【发布时间】:2010-09-06 04:55:05 【问题描述】:

我有兴趣学习一些(理想情况下)与数据库无关的从数据库表中选择第 n 行的方法。看看如何使用以下数据库的本机功能来实现这一点也很有趣:

SQL 服务器 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 的功劳:Firoz Ansari's Weblog

更新:有关 SQL 标准,请参阅 Troels Arvin's answer。 Troels,你有我们可以引用的链接吗?

【问题讨论】:

是的。以下是有关 ISO SQL 标准的信息链接:troels.arvin.dk/db/rdbms/links/#standards 只是要指出,通过关系的定义,表中的行没有顺序,所以不能选择表中的第N行。可以选择的是查询(其余部分)返回的行集中的第 N 行,这是您的示例和所有其他答案所完成的。对大多数人来说,这可能只是语义,但它指出了问题的根本问题。如果确实需要返回 OrderNo N ,则在表中引入 OrderSequenceNo 列,并在创建新订单时从独立序列生成器生成它。 SQL 标准定义了选项offset x fetch first y rows only。目前由(至少)Postgres、Oracle12、DB2 支持。 【参考方案1】:

在标准的可选部分中有这样做的方法,但很多数据库都支持自己的方法。

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 在 2018 年 9 月 15 日的 3.25.0 版本中添加了窗口函数支持,因此这两种形式也可以在 SQLite 中使用。

【讨论】:

MySQL 也使用 OFFSET 和 LIMIT 语法。 Firebird 使用 FIRST 和 SKIP 关键字,但它们被放置在 SELECT 之后。 不应该是WHERE rownumber = n 只获取第n行吗? MySQL 从版本 8 开始支持窗口函数。MariaDB 从版本 10.2 开始【参考方案2】:

PostgreSQL 支持 SQL 标准定义的windowing functions,但它们很尴尬,所以大多数人使用(非标准)LIMIT / OFFSET

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

此示例选择第 21 行。 OFFSET 20 告诉 Postgres 跳过前 20 条记录。如果您不指定ORDER BY 子句,则无法保证您将返回哪条记录,这很少有用。

【讨论】:

【参考方案3】:

我不确定其余的,但我知道 SQLite 和 MySQL 没有任何“默认”行排序。至少在这两种方言中,以下 sn-p 从 the_table 中获取第 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 其实,从这里php.about.com/od/mysqlcommands/g/Limit_sql.htm,如果你想抢第15个条目你不做LIMIT 14, 1(第0个是第一个元素,长度为1 应该是 SELECT * FROM the_table ORDER BY added DESC LIMIT 15,1【参考方案4】:

SQL 2005 及更高版本已内置此功能。使用 ROW_NUMBER() 函数。它非常适合具有 > 样式浏览的网页:

语法:

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

【讨论】:

我更喜欢这个解决方案,因为它感觉更直接。【参考方案5】:

我怀疑这非常低效,但这是一种非常简单的方法,它适用于我尝试过的一个小数据集。

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 上有一个客户端)具体来说,我将用循环的迭代器变量,并使用它依次复制和修改表的每一行。也许有人会看到这条评论并觉得这很有用【参考方案6】:

在 SQL Server 上验证它:

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

这将为您提供第 10 行 emp 表!

【讨论】:

你已经提供了这个问题的答案here删除你认为不正确的答案。如果你认为两个答案都是正确的,那么把两个答案都贴在一个地方【参考方案7】:

1 个小变化:n-1 代替 n。

select *
from thetable
limit n-1, 1

【讨论】:

哪种技术?【参考方案8】:

与某些答案声称的相反,SQL 标准并未就此主题保持沉默。

自 SQL:2003 以来,您已经能够使用“窗口函数”来跳过行和限制结果集。

在 SQL:2008 中,添加了一个稍微简单的方法,使用 OFFSET skip ROWS FETCH FIRST n ROWS ONLY

就我个人而言,我不认为真的需要添加 SQL:2008,所以如果我是 ISO,我会保持它脱离已经相当大的标准。

【讨论】:

很高兴有一个标准,让像我这样的人的生活更轻松,微软以标准的方式做事真是太好了:)【参考方案9】:

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

【讨论】:

【参考方案10】:

当我们过去在 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:请注意OuterPageSize 计算下方的IF / ELSE IF 块 - 在第 1 页和第 2 页上,它们会将 OuterPageSize 值降回 10。在第 3 页(第 21 行- 25) 计算将正确返回 5,并且在所有第 4 页和更大的页面上,计算的负数结果将被 0 替换(尽管此时立即返回空数据行可能会更快)。跨度> 哦,我现在明白了。好吧,我坚持我的观点,今天使用 MS SQL Server 2000 是不值得的。【参考方案11】:

在 Oracle 12c 中,您可以使用 OFFSET..FETCH..ROWS 选项和 ORDER BY

例如,从顶部获取第三条记录:

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

【讨论】:

【参考方案12】:

甲骨文:

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

【讨论】:

where ROWNUM = x 仅适用于 Oracle DB 中的 x = 1。即where ROWNUM = 2 不会返回任何行。【参考方案13】:

这是您困惑的快速解决方案。

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

在这里,您可以通过填充 N=0 获得最后一行,通过 N=1 获得倒数第二行,通过填充 N=3 获得倒数第四等等。

这是面试中非常常见的问题,而且非常简单。

如果您想要金额、ID 或一些数字排序顺序,则可以使用 MySQL 中的 CAST 函数。

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

这里通过填写 N = 4,您将能够从 CART 表中获得最高金额的倒数第五条记录。您可以适合您的字段和表名并提出解决方案。

【讨论】:

【参考方案14】:

添加:

LIMIT n,1

这会将结果限制为从结果 n 开始的一个结果。

【讨论】:

【参考方案15】:

例如,如果要在 MSSQL 中每 10 行选择一次,可以使用;

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

只要拿 MOD 并在这里更改数字 10 任何你想要的数字。

【讨论】:

【参考方案16】:

对于 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。

【讨论】:

【参考方案17】:

这是我最近为 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);

【讨论】:

【参考方案18】:

但是说真的,这一切不都是为了好的数据库设计而摆在首位的小把戏吗?有几次我需要这样的功能,它是一个简单的一次性查询来制作快速报告。对于任何真正的工作,使用这样的技巧都会招来麻烦。如果需要选择特定的行,那么只需使用具有顺序值的列即可。

【讨论】:

【参考方案19】:

对于 SQL server,以下将返回给定表的第一行。

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;

【讨论】:

【参考方案20】:

LIMIT n,1 在 MS SQL Server 中不起作用。我认为它只是不支持该语法的唯一主要数据库。公平地说,它不是 SQL 标准的一部分,尽管它得到了如此广泛的支持,它应该是。除了 SQL server LIMIT 之外的所有东西都很好用。对于 SQL Server,我还没有找到一个优雅的解决方案。

【讨论】:

除了Oracle、DB2,全世界几乎所有的企业级数据库。 PostgreSQL 是唯一支持 LIMIT 关键字的企业级数据库,这主要是因为它是开源的,它需要被忽略 ACID 的 MySQL 人群所接受。 @AlexD 这个“答案”是在实施 cmets 之前的 *** 旧时代发布的。我会将此作为对另一个答案的评论发布,但当时并不存在 cmets。【参考方案21】:

在 Sybase SQL Anywhere 中:

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

不要忘记 ORDER BY,否则它毫无意义。

【讨论】:

【参考方案22】:

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

例如要从 Employee 表中选择第 5 条记录,您的查询应该是

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

【讨论】:

【参考方案23】:

没什么花哨的,没有特殊功能,以防你像我一样使用 Caché...

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

假设您有一个可以信任的 ID 列或 datestamp 列。

【讨论】:

【参考方案24】:
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

【讨论】:

【参考方案25】:

我在这里参加聚会有点晚了,但我已经完成了这项工作,无需开窗或使用

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

【讨论】:

【参考方案26】:
SELECT * FROM emp a
WHERE  n = ( 
            SELECT COUNT( _rowid)
            FROM emp b
            WHERE a. _rowid >= b. _rowid
            );

【讨论】:

【参考方案27】:

难以置信你能找到执行这个的 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

【讨论】:

【参考方案28】:
select * from 
(select * from ordered order by order_id limit 100) x order by 
x.order_id desc limit 1;

首先按升序选择前 100 行,然后按降序选择最后一行并限制为 1。但是这是一个非常昂贵的语句,因为它会访问数据两次。

【讨论】:

【参考方案29】:

在我看来,为了提高效率,您需要 1) 生成一个介于 0 和比数据库记录数少 1 之间的随机数,以及 2) 能够选择该位置的行。不幸的是,不同的数据库有不同的随机数生成器和在结果集中的某个位置选择行的不同方法——通常您指定要跳过多少行以及想要多少行,但对于不同的数据库,它的执行方式不同。这是在 SQLite 中对我有用的东西:

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

它确实取决于能够在限制子句中使用子查询(在 SQLite 中是 LIMIT ,)选择表中的记录数应该特别有效,成为一部分数据库的元数据,但这取决于数据库的实现。另外,我不知道查询是否会在检索第 N 条记录之前实际构建结果集,但我希望它不需要。请注意,我没有指定“order by”子句。如果数据库无法在不构建结果集的情况下从数据库本身获取第 N 条记录,则最好“按”诸如主键之类的东西“排序”,它将有一个索引 - 从索引中获取第 N 条记录可能会更快.

【讨论】:

【参考方案30】:

我在this sql server 文章中看到的最合适的答案

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

【讨论】:

以上是关于如何选择 SQL 数据库表中的第 n 行?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 T-SQL 中的表中选择前 N 行?

MS Access 选择表中的第 N 个最高值

如何从T-SQL中的排序表中从第M行开始获取N行

SQL查询从通配符列等于值的所有表中选择所有行[重复]

SQL查询从通配符列等于值的所有表中选择所有行[重复]

三大数据库如何获取表中的第m条到第n条记录(n大于m)