我对学习从数据库表中选择第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的信用:Firoz Ansari的Weblog

更新:有关SQL标准的信息,请参见Troels Arvin的回答。 Troels,您有我们可以引用的任何链接吗?

评论

是。以下是有关ISO SQL标准信息的链接:troels.arvin.dk/db/rdbms/links/#standards

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

SQL标准定义了选项offset x仅提取前y行。当前受(至少)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


(我从上面链接的站点复制了该文件,因为我从未使用过这些DBs)

更新:从PostgreSQL 8.4开始,支持标准的窗口功能,因此希望第二个示例也适用于PostgreSQL。

更新:SQLite在3.25.0版上添加了窗口功能支持2018-09-15所以两种形式都可以在SQLite中使用。

评论


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

–道格
2011年12月1日于17:03

难道不是WHERE rownumber = n仅获得第n行?

–史蒂夫·贝内特(Steve Bennett)
17年9月12日在7:14

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

– Paul Spiegel
19年7月25日在20:01

#2 楼

PostgreSQL支持SQL标准定义的窗口功能,但是它们很笨拙,因此大多数人使用(非标准)LIMIT / OFFSET

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


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

#3 楼

我不确定其余的任何内容,但是我知道SQLite和MySQL没有任何“默认”行顺序。至少在这两种方言中,以下摘录从the_table中抓取了第15个条目,并按其添加的日期/时间进行了排序:

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


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

评论


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

–角斗士
2014年7月23日下午13:11

15是什么意思?我知道1表示获得一项记录。我检出1keydata.com/sql/sql-limit.html的示例中未使用逗号

–committedandroider
15年2月6日在6:25

实际上,从这里php.about.com/od/mysqlcommands/g/Limit_sql.htm,如果您想获取第15个条目,就不会做LIMIT 14,1(第0个元素是第一个元素,长度为1

–committedandroider
15年2月6日在6:27

应该是SELECT * FROM the_table ORDER BY添加DESC LIMIT 15,1

– GorvGoyl
16-4-27的6:28

#4 楼

SQL 2005及更高版本具有此内置功能。使用ROW_NUMBER()函数。对于具有<<上一页和下一页>>样式浏览的网页来说,它非常有用:

语法:

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


评论


我更喜欢这种解决方案,因为它感觉更简单。

– FoxArc
19/12/4在15:37

#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项,即更改第二个最高编号以获得另一个nth项。

仅SQL Server(我认为),但应该在不支持ROW_NUMBER()的旧版本上工作。

评论


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

–反向
13年5月17日在20:02



#6 楼

1个小改动:用n-1代替n。

select *
from thetable
limit n-1, 1


评论


哪种技术?

–user230910
18年1月15日在1:21

#7 楼

在SQL Server上验证它:

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


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

评论


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

– SpringLearner
14-10-16在11:04

#8 楼

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

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

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

我个人认为我并不需要真正添加SQL:2008,所以如果我是ISO,因此本来可以避免它已经超出相当大的标准。

评论


有一个标准虽然很好,但是使像我这样的人的生活更轻松,因此,Microsoft也可以以一种标准的方式来做事情:)

–user230910
18年1月15日在2:08

#9 楼


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


#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行,这显然是错误的。

– Bill Karwin
2011-12-30 8:53

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

– Bill Karwin
2011-12-30 17:14

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

– Bill Karwin
2011-12-30 17:15

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

–亚当五世
2012年1月1日,凌晨1:50

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

– Bill Karwin
2012年1月1日在2:57

#11 楼

甲骨文公司:

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


评论


其中ROWNUM = x仅适用于Oracle DB中的x = 1。即ROWNUM = 2时将不返回任何行。

–aff
19年7月11日在1:18

#12 楼

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

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

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


#13 楼

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

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


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

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

如果您想要的金额,ID或一些数字排序顺序比您想的要多MySQL中的CAST函数。在这里,通过填充N = 4,您将能够从CART表中获得最高金额的第五个最后记录。您可以适合您的字段和表名称并提出解决方案。

#14 楼

ADD:

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行的信息。请确保随后将行数设为0。

#17 楼

LIMIT n,1在MS SQL Server中不起作用。我认为这是唯一不支持该语法的主要数据库。公平地说,它不是SQL标准的一部分,尽管它得到了应有的广泛支持。除了SQL Server之外,LIMIT在所有情况下都非常有效。对于SQL Server,我一直找不到合适的解决方案。

评论


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

–大卫
2009年3月6日13:39

@AlexD这个“答案”是在Stackoverflow的旧版本中发布的,而在实施注释之前。我本可以将其发布为对另一个答案的评论,但是在那时,评论不存在。

– Kibbee
2012年8月28日在16:33

#18 楼

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


#19 楼

但是,真的,所有这些难道不是仅仅是为了获得良好的数据库设计而已?几次我需要这样的功能,这是一个简单的一次性查询即可做出快速报告。对于任何实际工作,使用这样的技巧都会引起麻烦。如果需要选择特定的行,则只需要一个具有顺序值的列并对其进行处理即可。

#20 楼

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


#21 楼

在Sybase SQL Anywhere中:

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


别忘了ORDER BY还是毫无意义。

#22 楼

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

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 楼

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


#24 楼

如果您像我一样使用Caché,那就没什么花哨的,没有特殊的功能了。

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


鉴于您拥有可以信任的ID列或datestamp列。

#25 楼

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


#26 楼

我在这里参加聚会有点晚了,但是我不需要窗口或使用它即可完成此操作。
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中有效

#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 楼

这就是我在DB2 SQL中的操作方式,我相信R / R(相对记录号)是由O / S存储在表中的;

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


#29 楼

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


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

#30 楼

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

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


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