SQL Server选择最后N行

202

这是一个已知的问题,但我找到的最佳解决方案类似于:

SELECT TOP N *
FROM MyTable
ORDER BY Id DESC

我有一张有很多行的表。不可能使用那个查询语句因为它需要很长的时间。那么如何选择最后N行而不使用ORDER BY?

编辑

抱歉,这是重复的问题


“last N” 是什么意思?如果没有顺序,“last N” 就没有多大意义。如果你的意思是“最后插入的 N 条记录”,那么你不能依靠 SQL Server 来提供这个 - 你必须使用 ORDER BY 子句。 - Daniel Renshaw
@Daniel Renshaw:获取表格中最后N个元素,而无需强制SQL Server对整个表进行排序,因为这会导致速度变得非常慢。 - Diego
2
你问题中的查询 最好的方法。如果 id 已经被索引,那么它只需要反向扫描该索引并在找到前5行后停止。如果没有被索引,那么它将需要进行 TOP N 排序。这不会比其他任何方法更糟糕。它不会对整个表进行排序(尽管需要扫描整个表)。 - Martin Smith
你为什么会将一个使用排序的分区答案标记为接受的答案?这并不能比原始查询更好。它只是在查询中添加了细微差别 - 即由特定客户的最后 N 个。你的查询不需要任何分区。 - onefootswill
根据我的经验,在MS SQL Server中使用ROW_NUMBER OVER PARTITION BY来执行这种类型的查询要比其他方法快得多。所以,就我个人而言,很高兴它被标记为被接受的答案,其他条件都相同的情况下。 - Reversed Engineer
好吧。他最初的查询没有WHERE子句,所以不确定如何进行分区。如果Id是一个聚集索引,我不确定对整个集合进行分区是否会更快。但我还没有尝试过这个,所以也许... - onefootswill
20个回答

154

您可以使用以下查询语句让SQL Server选择最后N行:

select * from tbl_name order by id desc limit N;

3
版本兼容性如何? - Fractaliste
97
这在SQL Server中不起作用。看起来是MySQL、PostgreSQL和SQLite的特性。 - Tim Friesen
4
所有列举的产品都是 SQL 服务器。 如果您想谈论 MS SQL Server,为什么不直接说呢? - gena2x
7
我有点困惑,这个问题要求如何创建一个“不使用ORDER BY”的选择查询,而你回答中的选择查询却有“order by”。这是一种没有使用“order by”的“order by”吗? - Robert Sinclair
8
@gena2x,这个问题标记为 SQL Server。该标记指的是 Microsoft SQL Server。 - Martin Smith
显示剩余3条评论

64

我测试了JonVD的代码,但发现它非常慢,需要6秒。

这段代码只需0秒。

SELECT TOP(5) ORDERID, CUSTOMERID, OrderDate    
FROM Orders where EmployeeID=5    
Order By OrderDate DESC

5
多少行?当你有很多行时,程序可能会变得非常慢。 - Diego
@Diego 为什么会这样呢?如果你已经对 OrderDate 进行了索引,那么选择查询的前 N 行或后 N 行应该是同样快的。我知道 OrderDate 可能与插入顺序相关,但这最多只是一个副作用,并且仍然需要进行表扫描,不是吗?(而且我认为它并没有回答 OP 所指出的 更好的重复问题:即在不排序的情况下选择最后 5 行) - ruffin
1
@Diego - 你为什么认为这个答案会比你接受的答案更慢? - Martin Smith
3
这会将行倒过来排列。然后您需要通过重新排序它们来恢复原始顺序。 - Mark

42
你可以使用PARTITION中的ROW NUMBER功能来实现。可以在 这里 找到一个很好的例子:

我正在使用Northwind数据库的订购表... 现在,让我们检索由Employee 5放置的最后5个订单:

SELECT ORDERID, CUSTOMERID, OrderDate
FROM
(
    SELECT ROW_NUMBER() OVER (PARTITION BY EmployeeID ORDER BY OrderDate DESC) AS OrderedDate,*
    FROM Orders
) as ordlist

WHERE ordlist.EmployeeID = 5
AND ordlist.OrderedDate <= 5

1
ROW NUMBER BY PARTITION 功能也需要排序。您需要对表进行排序,以为每个记录分配行号... - Sadhir
这是正确的,但如果没有某种排序方式,它就不会起作用,最好的解决方案是索引被查询的主要列,并使用类似上面的查询运行。 - JonVD

19

如果您想从表中选择最后几行的数据。

语法将如下所示

 select * from table_name except select top 
 (numbers of rows - how many rows you want)* from table_name

这些语句能够工作,但它们的方式不同。谢谢大家。

 select * from Products except select top (77-10) * from Products

通过这种方式,您可以获取最后的10行,但是顺序将以降序显示

select top 10 * from products
 order by productId desc 

 select * from products
 where productid in (select top 10 productID from products)
 order by productID desc

 select * from products where productID not in 
 (select top((select COUNT(*) from products ) -10 )productID from products)

12

非常一般化的说,为了支持SQL服务器,这里是:

SELECT TOP(N) *
FROM tbl_name
ORDER BY tbl_id DESC

就性能而言,它并不差(在服务器上处理超过10,000条记录不到一秒钟)。


3
当你需要关注性能时,1万条记录其实微不足道。只有当你开始处理数百万条记录时,才需要考虑性能问题。 - Dom84

10

首先,您需要从中获取记录计数

 Declare @TableRowsCount Int
 select @TableRowsCount= COUNT(*) from <Your_Table>

然后:

在SQL Server 2012中

SELECT *
FROM  <Your_Table> As L
ORDER BY L.<your Field>
OFFSET <@TableRowsCount-@N> ROWS
FETCH NEXT @N ROWS ONLY;

在SQL Server 2008中

SELECT *
FROM 
(
SELECT ROW_NUMBER() OVER(ORDER BY ID) AS sequencenumber, *
FROM  <Your_Table>
    Order By <your Field>
) AS TempTable
WHERE sequencenumber > @TableRowsCount-@N 

6
"Id"是否被索引了?如果没有,那就是需要做的重要事情(我猜它已经被索引了)。
此外,您是否需要返回所有列?如果您实际上只需要一个较小的列子集,并且可以完全由ID列上的索引满足-例如,如果您在ID列上有一个非聚集索引,没有其他字段包含在索引中,则必须在群集索引上进行查找以获取其余的列来返回,这可能会占用查询成本的很大一部分。如果它是聚集索引或包含您想在查询中返回的所有其他字段的非聚集索引,则应该没问题。"

6
select * from (select top 6 * from vwTable order by Hours desc) T order by Hours

4

我使用的一种查询大型表中最近行的技术是将查询限制为仅“读取”最近“N”个百分比的行。这是现实世界的应用,例如,我针对非历史性的最新天气数据、最新新闻订阅或最新GPS位置数据点进行此操作。

如果您确信自己的行位于表的最近前5%(例如),那么这将是一个巨大的性能提升。即使表上有索引,它也只会进一步将可能性限制在具有1亿个或10亿个行的表中的5%的行。当较旧的数据需要进行物理磁盘读取而不仅仅是逻辑内存读取时,情况就尤其如此。

与SELECT TOP | PERCENT | LIMIT相比,这要更加高效,因为它不会选择行,而仅限制要搜索的数据部分。

DECLARE @RowIdTableA BIGINT
DECLARE @RowIdTableB BIGINT
DECLARE @TopPercent FLOAT

-- Given that there is an Sequential Identity Column
-- Limit query to only rows in the most recent TOP 5% of rows
SET @TopPercent = .05
SELECT @RowIdTableA = (MAX(TableAId) - (MAX(TableAId) * @TopPercent)) FROM TableA
SELECT @RowIdTableB = (MAX(TableBId) - (MAX(TableBId) * @TopPercent)) FROM TableB

SELECT *
FROM TableA a
INNER JOIN TableB b ON a.KeyId = b.KeyId
WHERE a.Id > @RowIdTableA AND b.Id > @RowIdTableB AND
      a.SomeOtherCriteria = 'Whatever'

一个有趣的方法 - Majid Qafouri
1
这个查询很好,但需要放在SQL存储过程中。如果你想要一个更简单的查询,可以用括号中的SELECT语句替换变量的使用。 SELECT Col1 FROM Table1 WHERE TableIdCol > (SELECT MAX(TableIdCol)-20000 from Table1) ORDER BY TableIdCol1; - Rashi Abramson

4

以下是一种不需要使用 order by 的方法,但我认为它要求每行都是唯一的。其中 N 表示你想要的行数,L 是表中的行数。

select * from tbl_name except select top L-N * from tbl_name

如前所述,返回哪些行是未定义的。

编辑:实际上这非常缓慢。真的没有什么价值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接