关于mysql:如何选择SQL数据库表中的第n行?

How to select the nth row in a SQL database table?

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

  • SQL服务器
  • MySQL
  • 《PostgreSQL》
  • 数据库
  • 甲骨文公司

我目前正在SQL Server 2005中执行以下操作,但我希望看到其他更不可知论的方法:

1
2
3
4
5
6
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的日志

更新:参见TrolesArvin关于SQL标准的回答。特洛伊,你有我们可以引用的链接吗?


在标准的可选部分中有一些方法可以做到这一点,但是许多数据库支持它们自己的方法。

一个非常好的站点,可以谈论这个和其他事情,它是http://troels.arvin.dk/db/rdbms/select limit。

PostgreSQL和MySQL基本上支持非标准:

Oracle、DB2和MSSQL支持标准窗口功能:

1
2
3
4
5
6
7
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中工作。


PostgreSQL中的LIMIT/OFFSET语法为:

1
2
3
4
5
6
7
SELECT
    *
FROM
    mytable
ORDER BY
    somefield
LIMIT 1 OFFSET 20;

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

显然,在疯狂的窗口函数之外,SQL标准对限制问题保持沉默,这就是为什么每个人都不同地实现它的原因。


我不确定其余的内容,但我知道sqlite和mysql没有任何"默认"行排序。至少在这两种方言中,以下代码片段从_表中获取第15个条目,并按添加的日期/时间排序:

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

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


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

语法:

1
2
3
4
5
6
7
8
9
10
11
12
SELECT
    *
FROM
    (
        SELECT
            ROW_NUMBER () OVER (ORDER BY MyColumnToOrderBy) AS RowNum,
            *
        FROM
            Table_1
    ) sub
WHERE
    RowNum = 23


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

这将得到第五个项目,更改第二个顶部编号以获得不同的第n个项目

SQL Server只能(我认为)运行在不支持row_number()的旧版本上。


在SQL Server上验证:

1
2
3
Select top 10 * From emp
EXCEPT
Select top 9 * From emp

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


1小变化:N-1而不是N。

1
2
3
select *
from thetable
limit n-1, 1


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

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

在SQL:2008中,增加了一种稍微简单的方法,即使用OCx1(4)

就我个人而言,我不认为真正需要添加SQL:2008,所以如果我是ISO,我会将它排除在已经相当大的标准之外。


SQL SERVER

从顶部选择第n条记录

1
2
3
4
5
6
SELECT * FROM (
SELECT
ID, NAME, ROW_NUMBER() OVER(ORDER BY ID) AS ROW
FROM TABLE
) AS TMP
WHERE ROW = n

从底部选择第n条记录

1
2
3
4
5
6
SELECT * FROM (
SELECT
ID, NAME, ROW_NUMBER() OVER(ORDER BY ID DESC) AS ROW
FROM TABLE
) AS TMP
WHERE ROW = n


神谕:

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


当我们以前在MSSQL2000中工作时,我们做了我们称之为"三重翻转"的工作:

编辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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

它不优雅,速度也不快,但很管用。


这是一个快速解决你困惑的方法。

这里,您可以通过填充n=0得到最后一行,第二行最后一行填充n=1,第四行最后一行填充n=3,依此类推。

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

此外,如果您需要数量、ID或一些数字排序顺序,而不是您在MySQL中使用CAST函数。

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

在这里,填写n=4,您将能够从购物车表中获得金额最高的第五个最后记录。您可以调整字段和表名,并提出解决方案。


添加:

1
LIMIT n,1

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


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

1
2
3
4
5
6
SELECT * FROM (
  SELECT
    ROW_NUMBER() OVER (ORDER BY ColumnName1 ASC) AS rownumber, ColumnName1, ColumnName2
  FROM TableName
) AS foo
WHERE rownumber % 10 = 0

带上mod,把数字10改成你想要的任何数字。


这是我最近为Oracle编写的存储过程的通用版本,它允许动态分页/排序-hth

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
-- 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);


但是,真的,这不是所有这些仅仅是好的数据库设计的会客室技巧吗?有几次我需要这样的功能,一个简单的一次性查询就可以快速生成报告。对于任何真正的工作,使用这样的技巧都会招来麻烦。如果需要选择一个特定的行,那么只需要有一个具有连续值的列,并用它来完成。


限制n,1在MS SQL Server中不起作用。我认为这是唯一一个不支持这种语法的主要数据库。公平地说,它不是SQL标准的一部分,尽管它得到了广泛的支持,应该是这样的。除了SQL Server限制,其他方面都很好。对于SQL Server,我还没有找到一个优雅的解决方案。


在Oracle 12c中,您可以将OFFSET..FETCH..ROWS选项与ORDER BY一起使用。

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

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


对于SQL Server,按行号排序的一般方法如下:

1
SET ROWCOUNT @row --@row = the row number you wish to work on.

例如:

1
2
3
4
5
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。


在Sybase SQL Anywhere中:

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

别忘了点菜,要不然就没意义了。


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


T-SQL-从表中选择第n个记录编号

1
2
3
4
5
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条记录,您的查询应该是

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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行。这个查询的例子是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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


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

1
2
3
4
declare @rowNumber int = 1;
    select TOP(@rowNumber) * from [dbo].[someTable];
EXCEPT
    select TOP(@rowNumber - 1) * from [dbo].[someTable];

您可以通过如下方式循环遍历这些值:

1
2
3
4
5
6
7
8
9
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;


没有花哨,没有特殊功能,以防你像我一样使用Cach_…

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

假设您有一个ID列或一个日期戳列,您可以信任它。


这就是我在DB2SQL中实现的方法,我相信RRN(相对记录号)是由O/S存储在表中的;

1
2
3
4
5
SELECT * FROM (                        
   SELECT RRN(FOO) AS RRN, FOO.*
   FROM FOO                        
   ORDER BY RRN(FOO)) BAR            
 WHERE BAR.RRN = recordnumber


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

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


在我看来,为了提高效率,您需要1)生成一个小于数据库记录数0到1之间的随机数,2)能够选择该位置的行。不幸的是,不同的数据库具有不同的随机数生成器和在结果集中的某个位置选择行的不同方法-通常您指定要跳过的行数和所需的行数,但对于不同的数据库则不同。以下是在sqlite中对我有用的东西:

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

它确实依赖于能够在limit子句中使用子查询(在sqlite中是limit)选择表中的记录数应该特别有效,这是数据库元数据的一部分,但这取决于数据库的实现。另外,我不知道查询是否会在检索第n条记录之前构建结果集,但我希望它不需要。请注意,我没有指定"ORDERBY"子句。像主键这样有索引的"排序方式"可能更好——如果数据库在没有生成结果集的情况下无法从数据库本身获取第n条记录,则从索引中获取第n条记录可能更快。


令人难以置信的是,你能找到一个执行这个任务的SQL引擎…

1
2
3
4
5
6
7
8
9
10
11
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