我有兴趣学习一些(理想情况下)与数据库无关的选择方法 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 的功劳: 菲罗兹·安萨里的博客

更新:特罗尔斯·阿尔文的回答 关于 SQL 标准。 Troels,你有我们可以引用的链接吗?

有帮助吗?

解决方案

标准的可选部分有多种方法可以做到这一点,但许多数据库都支持自己的方式。

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

其他提示

LIMIT / OFFSET PostgreSQL 中的语法 是:

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

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

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

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

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

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

SQL 2005 及以上版本内置了此功能。使用 ROW_NUMBER() 函数。它非常适合具有 << Prev and Next >> 风格浏览的网页:

句法:

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

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

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

在 SQL Server 上验证:

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

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

1个小改动:n-1 而不是 n。

select *
from thetable
limit n-1, 1

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

从 SQL:2003 开始​​,您已经能够使用“窗口函数”来跳过行并限制结果集。

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

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

甲骨文:

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

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

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

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

这里有一个快速解决您困惑的方法。

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表中获得最高金额的倒数第五条记录。您可以调整您的字段和表名称并提出解决方案。

添加:

LIMIT n,1

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

例如,如果您想在 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 更改为任何您想要的数字即可。

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

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

但实际上,这一切难道不只是良好数据库设计的小技巧吗?有几次我需要这样的功能,只是为了进行简单的一次性查询以快速生成报告。对于任何实际工作来说,使用这样的技巧都会带来麻烦。如果需要选择特定行,则只需选择具有顺序值的列即可完成。

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

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

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

在 Sybase SQL Anywhere 中:

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

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

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
SELECT * FROM emp a
WHERE  n = (SELECT COUNT( _rowid)
              FROM emp b
             WHERE a. _rowid >= b. _rowid);
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

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

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

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

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

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

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

SELECT * FROM (                        
   SELECT RRN(FOO) AS RRN, FOO.*
   FROM FOO                         
   ORDER BY RRN(FOO)) BAR             
 WHERE BAR.RRN = recordnumber
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 中对我有用的东西:

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

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

对于 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;
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top