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

时间:2008-08-19 17:13:11

标签: mysql sql database oracle postgresql

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

  • SQL Server
  • 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,您有任何我们可以引用的链接吗?

33 个答案:

答案 0 :(得分:308)

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

一个非常好的网站,讨论这个和其他事情是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。

答案 1 :(得分:89)

LIMIT / OFFSET syntax in PostgreSQL是:

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

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

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

答案 2 :(得分:27)

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

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

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

答案 3 :(得分:19)

SQL 2005及更高版本内置了此功能。使用ROW_NUMBER()函数。对于具有&lt;&lt;&lt;&lt;&lt;&lt;&lt;上一个和下一个&gt;&gt;风格浏览:

语法:

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

答案 4 :(得分:16)

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

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()的旧版本。

答案 5 :(得分:12)

在SQL Server上验证它:

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

这将给你第10张emp表的行!

答案 6 :(得分:11)

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

select *
from thetable
limit n-1, 1

答案 7 :(得分:9)

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

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

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

添加了一种稍微简单的方法

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

答案 8 :(得分:6)

当我们以前在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

它不优雅,并不快,但它有效。

答案 9 :(得分:6)

甲骨文:

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

答案 10 :(得分:6)

  

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

答案 11 :(得分: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表中获得最高金额的第五个最后记录。您可以使用您的字段和表名称并提出解决方案。

答案 12 :(得分:3)

ADD:

LIMIT n,1

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

答案 13 :(得分: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就可以了。

答案 14 :(得分:2)

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

答案 15 :(得分:2)

在Oracle 12c中,您可以将OFFSET..FETCH..ROWS选项与ORDER BY

一起使用

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

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

答案 16 :(得分:2)

但实际上,首先不是所有这些都只是用于良好数据库设计的客厅技巧吗?我需要几次这样的功能,这是一个简单的一次性查询来快速报告。对于任何实际工作,使用这样的技巧会引起麻烦。如果需要选择一个特定的行,那么只需要一个具有顺序值的列并完成它。

答案 17 :(得分: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);

答案 18 :(得分: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

答案 19 :(得分:1)

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

答案 20 :(得分:1)

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

答案 21 :(得分:1)

在Sybase SQL Anywhere中:

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

不要忘记ORDER BY或它没有意义。

答案 22 :(得分: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

答案 23 :(得分:1)

我在这里参加聚会有点晚了,但是我这样做不需要窗口或使用

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中有效

答案 24 :(得分:0)

WITH r AS (
  SELECT TOP 1000 * FROM emp
)
SELECT * FROM r
EXCEPT
SELECT TOP 999 FROM r

这将在SQL Server中给出第1000行。

答案 25 :(得分:0)

如果要查看本机功能: MySQL,PostgreSQL,SQLite和Oracle(基本上SQL Server似乎没有此功能),您实际上可以使用NTH_VALUE窗口函数。 Oracle资料来源:Oracle Functions: NTH_VALUE

我实际上已经在我们的Oracle数据库中进行了试验,以比较第一行(订购后)和第二行(再次订购后)。 代码看起来类似于此(如果您不想转到链接):

SELECT DISTINCT dept_id
     , NTH_VALUE(salary,2) OVER (PARTITION BY dept_id ORDER BY salary DESC
           RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) 
        AS "SECOND HIGHEST"
     , NTH_VALUE(salary,3) OVER (PARTITION BY dept_id ORDER BY salary DESC
           RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
        AS "THIRD HIGHEST"
  FROM employees
 WHERE dept_id in (10,20)
 ORDER 
    BY dept_id;

我发现它很有趣,希望他们让我使用它。

答案 26 :(得分:0)

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

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

答案 27 :(得分:0)

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

答案 28 :(得分:0)

在我看来,为了提高效率,你需要1)生成一个0到1之间的随机数,小于数据库记录的数量,2)能够选择该位置的行。遗憾的是,不同的数据库具有不同的随机数生成器以及在结果集中某个位置选择行的不同方法 - 通常指定要跳过的行数和所需的行数,但对于不同的数据库,它们的执行方式不同。这是在SQLite中适合我的东西:

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

它确实依赖于能够在limit子句中使用子查询(在SQLite中是LIMIT&lt; recs跳过&gt;,&lt; recs to take&gt;)选择表中的记录数应该特别有效,作为数据库元数据的一部分,但这取决于数据库的实现。此外,我不知道查询是否会在检索第N条记录之前实际构建结果集,但我希望它不需要。请注意,我没有通过&#34;指定&#34;顺序。条款。通过&#34;排序可能会更好。类似主键的东西,它将有一个索引 - 如果数据库无法构建结果集而无法从数据库本身获取第N条记录,那么从索引获取第N条记录可能会更快。

答案 29 :(得分:0)

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

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

答案 30 :(得分:0)

这就是我在DB2 SQL中的做法,我相信RRN(相对记录号)由O / S存储在表中;

sizeof( char * )

答案 31 :(得分:0)

没有什么特别的功能,如果你像我一样使用Caché......

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

鉴于您有一个可以信任的ID列或日期戳列。

答案 32 :(得分: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