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


399

我有兴趣学习一些(理想情况下)从数据库表中选择第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的Weblog

更新:有关SQL标准,请参见Troels Arvin的答案Troels,您有没有我们可以引用的链接?


3
是。这是有关ISO SQL标准信息的链接:troels.arvin.dk/db/rdbms/links/#standards
Troels Arvin,

13
只是要指出,根据关系的定义,表中的行没有顺序,因此无法选择表中的第N行。可以选择的是查询(其余)返回的行集中的第N行,这是您的示例和所有其他答案所完成的。在大多数情况下,这可能只是语义,但它指出了该问题的根本问题。如果确实需要返回OrderNo N,则在表中引入OrderSequenceNo列,并在创建新订单时从独立的序列生成器生成它。
丹尼尔·苏达雷维奇

2
SQL标准定义了option offset x fetch first y rows only。当前受(至少)Postgres,Oracle12,DB2支持。
a_horse_with_no_name

Answers:


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在2018-09-15的3.25.0版本中添加了窗口功能支持,因此两种形式都可以在SQLite中使用。


3
MySQL也使用OFFSET和LIMIT语法。Firebird使用FIRST和SKIP关键字,但是将它们放在SELECT之后。
道格

7
那不是WHERE rownumber = n只获得第n行吗?
史蒂夫·本内特

MySQL从版本8开始支持窗口功能。MariaDB从版本10.2开始
Paul Spiegel

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没有任何“默认”行顺序。至少在这两种方言中,以下片段从the_table中获取第15个条目,并按添加日期/时间排序:

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

(当然,您需要添加一个DATETIME字段,并将其设置为添加条目的日期/时间...)


这似乎是用内联偏移量值限制查询的最佳方法。但是我们不应该在这里使用0,14吗?1,15将离开第一行。
角斗士2014年

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

它应该是SELECT * FROM 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 Server(我认为),但应在不支持ROW_NUMBER()的旧版本上运行。


我将使用它,因为ROW_NUMBER()在SQL 2000中不起作用(是的,我们仍然在SQL 2000上有一个客户端)具体地说,我将用循环的迭代器变量替换'5'并使用依次复制和修改表格的每一行。也许有人会看到此评论并发现它有用
Inversus


14

在SQL Server上验证:

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

这将给您emp表的第10行!


您已经在此处提供了此问题的答案。删除您认为不合适的答案。如果您认为两个答案都正确,则将两个答案都放在一个位置
SpringLearner 2014年

11

与某些答案所声称的相反,SQL标准对此主题并没有保持沉默。

从SQL:2003开始,您就可以使用“窗口函数”跳过行并限制结果集。

在SQL:2008中,使用
OFFSET skip ROWS FETCH FIRST n ROWS ONLY

就我个人而言,我认为并不需要真正添加SQL:2008,因此,如果我是ISO,我将使它脱离本来就相当大的标准。


很好的是,尽管有一个标准,但是它使像我这样的人的生活变得更轻松,因此,Microsoft非常
乐于

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行,这显然是错误的。
Bill Karwin 2011年

现在,如果我们想要一个中间页面,它将不起作用。假设我们有25行,我们需要第二页,即11-20行。内部查询获取最上面的2 * 10 = 20行或1-20行。中间查询获得最后15行:25-((2-1)* 10)= 15,这产生了20-6行。最后一个查询将颠倒顺序,并返回6-20行。除非总行数是所需页面大小的倍数,否则此技术不起作用。
Bill Karwin

也许最好的结论是,我们应该升级所有剩余的MS SQL Server 2000实例。:-)快到2012年了,这个问题已经以更好的方式解决了很多年!
比尔·卡文

@Bill Karwin:请注意计算IF / ELSE IF下方的块OuterPageSize-在第1和2页上,它们会将OuterPageSize值降回10。在第3页(第21-25行)上,计算将正确返回5,在所有第4页及以上的页上,计算结果的负数将被0代替(尽管此时可能会更快地立即返回空数据行)。
2012年

哦,我明白了。好吧,我坚持认为,今天使用MS SQL Server 2000并不值得。
Bill Karwin

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

甲骨文:

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

1
where ROWNUM = x在Oracle DB中仅适用于x = 1。即where ROWNUM = 2不会返回任何行。
aff

5

在Oracle 12c中,您可以将OFFSET..FETCH..ROWS option与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来获得第四行,依此类推。

这是面试中非常普遍的问题,这是非常简单的答案。

更进一步,如果您想要数量,ID或某些数字排序顺序,则您可能不希望在MySQL中使用CAST函数。

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

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



3

例如,如果要在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号即可。


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 Server之外,LIMIT在所有情况下都非常有效。对于SQL Server,我一直找不到理想的解决方案。


1
除了Oracle,DB2以外,几乎全世界的每个企业级数据库都如此。PostgreSQL是唯一支持LIMIT关键字的企业级数据库,主要是因为它是开源的,需要ACID忽略的MySQL人群可以使用。
戴维

3
@AlexD这个“答案”是在Stackoverflow的早期版本中发布的,之前是未实施评论。我本可以将其发布为对另一个答案的评论,但在当时,评论不存在。
Kibbee 2012年

2

这是我最近为Oracle写的sproc的通用版本,它允许动态分页/排序-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 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;

1

在Sybase SQL Anywhere中:

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

不要忘记ORDER BY或它毫无意义。


1

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

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

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列或datestamp列,则可以信任。


0

这就是我在DB2 SQL中执行此操作的方式,我相信R / O(相对记录号)是由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到1之间的随机数,以及2)能够选择该位置的行。不幸的是,不同的数据库具有不同的随机数生成器,以及在结果集中某个位置选择行的方法也不同-通常您指定要跳过的行数和所需的行数,但是对于不同的数据库,它的处理方式有所不同。这在SQLite中对我有用:

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

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


0

最合适的答案,我看到的这个文章为SQL Server

WITH myTableWithRows AS (
    SELECT (ROW_NUMBER() OVER (ORDER BY myTable.SomeField)) as row,*
    FROM myTable)
SELECT * FROM myTableWithRows WHERE row = 3
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.