如何选择最底端的行?


100

我可以选择SELECT TOP(200)...但是为什么不选择BOTTOM(200)?

难道不是要理解我的意思是,我怎么能做相当于TOP(200)的结果,但相反(从底部开始,就像您期望BOTTOM所做的那样...)?

Answers:


89
SELECT
    columns
FROM
(
     SELECT TOP 200
          columns
     FROM
          My_Table
     ORDER BY
          a_column DESC
) SQ
ORDER BY
     a_column ASC

2
为什么使用派生表?
RichardOD

14
如果要按A-> Z顺序返回行,但是要按Z-> A顺序选择前200个行,则这是一种方法。其他答案表明仅更改ORDER BY不会返回问题中描述的相同结果,因为它们将乱序显示(除非顺序无关紧要,OP并没有说)。
汤姆H

3
@Tom H.必须思考您的意思几秒钟(我已经工作了14个小时)。起初,我看不出答案与您的订单之间的区别,但现在我可以了。所以+1。
RichardOD

1
当您在较小的桌子上工作时,此和其他答案都可以正常工作。当您只对底部的几行感兴趣时,我认为不应该按列对整个表进行排序。
steady鱼

1
好的答案,最好的恕我直言...真正的问题是我无法从ASP脚本执行此操作,所以我认为我需要手动重新排列objRecordset或使用ASP提供的功能....
Andrea_86

99

没必要。您可以使用,ORDER BY而只需更改排序DESC即可获得相同的效果。


3
谷歌说了同样的事情,现在你们9个人同意,对我来说足够好了,谢谢:D
CloudMeta

8
使用DESC将返回最后N行,但是返回的行也将与前N行相反。
RickNZ

11
如果表上没有指向ORDER BY的索引怎么办?
保护者2012年

8
@Justin:想象只有一列,包含varchar值。ORDER BY将按字母顺序排序,这(可能)不是我们想要的。
保护者1年1

3
Tom H.是正确的答案,否则,您的行将以相反的顺序排列。
Pierre-Olivier Goulet'14

39

抱歉,我认为我没有看到正确的答案。

TOPX功能显示在未确定的订单记录。根据该定义,BOTTOM无法定义函数。

独立于任何索引或排序顺序。当您执行an时,您将ORDER BY y DESC首先获得y值最高的行。如果这是一个自动生成的ID,则应显示其他记录中建议的最后添加到表中的记录。然而:

  • 仅在有自动生成的id列的情况下才有效
  • 如果将其与TOP功能进行比较,则会对性能产生重大影响

正确的答案应该是不存在,也不能等同TOP于获取最下面的行。


3
没错,似乎没有等效的解决方法。
2014年

4
TOP与将项目添加到表中的顺序无关,它只是意味着“给出与我的查询匹配的前X条记录”
路加福音

4
是的,它确实为您提供了记录中添加到您查询中的表的第一个记录。
Martijn Burger

4
我同意卢克。根据定义,数据库表是无序的。当select语句没有ORDER BY子句时,永远不要依赖RDBMS提供的顺序。在Wiki上阅读这里, 但是,除非查询表的SELECT语句中指定了ORDER BY子句,否则数据库系统不保证行的任何顺序。
Zohar Peled 2015年

2
我同意这个答案,但是实际上汤姆的答案解决了所有实际使用的问题。
Antonio

18

从逻辑上讲

BOTTOM (x) is all the records except TOP (n - x), where n is the count; x <= n

例如,从员工中选择前1000名:

在T-SQL中

DECLARE 
@bottom int,
@count int

SET @bottom = 1000 
SET @count = (select COUNT(*) from Employee)

select * from Employee emp where emp.EmployeeID not in 
(
SELECT TOP (@count-@bottom) Employee.EmployeeID FROM Employee
)

1
嗨shadi2014,如果不使用“ ORDER BY”,您的结果将是随机的。
bummi

5
笨蛋,你是对的,但这就是使这个答案正确的原因。从理论上讲,Select TOP本身是“随机的”,这是Select BOTTOM的正确实现。在5000条记录的表,底部1000,除了顶部4000的一切
tzachs

9

在解决方案中实现ORDER BY子句的任何答案似乎都没有抓住重点,或者实际上不了解TOP会给您带来什么。

TOP返回无序查询结果集,该结果集将记录集限制为返回的前N条记录。(从Oracle的角度看,这类似于在ROWNUM <(N + 1)处添加。

任何使用订单的解决方案都可能返回由TOP子句返回的行(因为数据集首先是无序的),具体取决于订单使用的条件

TOP的用处是,一旦数据集达到一定大小N,它将停止获取行。您无需获取所有数据即可了解数据的外观。

为了准确地实现BOTTOM,将需要无序获取整个数据集,然后将数据集限制为最终的N条记录。如果您要处理大表,那将不会特别有效。也不会必然给你什么,你认为你所要求的。数据集的末尾不一定是“最后插入的行”(对于大多数DML密集型应用程序可能不是)。

同样,不幸的是,实现ORDER BY的解决方案在处理大型数据集时可能会造成灾难性的后果。如果我有100亿条记录并想要最后10条记录,那么订购100亿条记录并选择最后10条记录是非常愚蠢的。

这里的问题是,当与TOP进行比较时,BOTTOM没有我们想到的含义。

一次又一次地插入,删除,插入,删除记录时,存储中将出现一些间隙,以后,如果可能,将插入行。但是,当我们选择TOP时,我们经常看到的似乎是已排序的数据,因为它可能早已插入到表的存在中。如果该表没有很多删除,则它似乎是有序的。(例如,创建日期可能早于表创建本身的时间)。但是实际情况是,如果这是一个大量删除的表,则前N行可能根本不像这样。

所以-这里的底线(双关语意味是)是要BOTTOM N记录的人实际上并不知道他们要的是什么。或者,至少,他们要的是什么以及BOTTOM的实际含义是不同的。

因此-解决方案可能满足请求者的实际业务需求...但是不满足成为BOTTOM的标准。


1
很好的解释。支持在该主题上进行更多讨论。
rohrl77 '19

我的用例是这个。我做了一个大insert声明,将行放入一个没有索引的大表中。(在开始为表编制索引之前,我先填充了该表。)由于重新启动或其他原因,我失去了客户端会话,现在我想看看是否有新添加的行。如果表的“底部”行是我最近的行之一,则我知道操作已完成。如果“底部”行是其他内容,那么不能保证,我必须扫描整个表以确保...但是我很可能可以通过快速检查“底部”来节省一些时间,就像您在“最佳'。
Ed Avis

很好的解释,但是它仍然暗示着底部的存在,只需要反向读取/检索数据即可。在新表上插入被中止的情况下(在公认的边缘),在不检索所有内容的情况下验证插入的最后一条记录(底部)将很有用。是否存在技术原因导致无法以相反的顺序检索表数据?
James

3

正如“保护者一”所指出的那样,“贾斯汀·埃斯蒂尔”当前接受的答案不是正确的答案。

据我所知,到目前为止,没有其他答案或评论提供与作者所要求的BOTTOM(x)相当的东西。

首先,让我们考虑一个需要此功能的场景:

SELECT * FROM Split('apple,orange,banana,apple,lime',',')

这将返回一个包含一列和五个记录的表:

  • 苹果
  • 橙子
  • 香蕉
  • 苹果
  • 酸橙

如您所见:我们没有ID列;我们不能按返回的列排序;而且我们无法像使用前两个记录那样使用标准SQL选择后两个记录。

这是我尝试提供的解决方案:

SELECT * INTO #mytemptable FROM Split('apple,orange,banana,apple,lime',',')
ALTER TABLE #mytemptable ADD tempID INT IDENTITY
SELECT TOP 2 * FROM #mytemptable ORDER BY tempID DESC
DROP TABLE #mytemptable

这里是一个更完整的解决方案:

SELECT * INTO #mytemptable FROM Split('apple,orange,banana,apple,lime',',')
ALTER TABLE #mytemptable ADD tempID INT IDENTITY
DELETE FROM #mytemptable WHERE tempID <= ((SELECT COUNT(*) FROM #mytemptable) - 2)
ALTER TABLE #mytemptable DROP COLUMN tempID
SELECT * FROM #mytemptable
DROP TABLE #mytemptable

我决不是声称在所有情况下都可以使用此方法,但可以提供预期的结果。



1

用另一种方式排序的问题是,它常常不能很好地利用索引。如果您需要选择许多不在开头或结尾的行,它也不是很可扩展。另一种方法如下。

DECLARE @NumberOfRows int;
SET @NumberOfRows = (SELECT COUNT(*) FROM TheTable);

SELECT col1, col2,...
FROM (
    SELECT col1, col2,..., ROW_NUMBER() OVER (ORDER BY col1) AS intRow
    FROM TheTable
) AS T
WHERE intRow > @NumberOfRows - 20;

2
1)如果更改ORDER BY子句的方向“没有充分利用索引”,那么获得一个不错的RDBMS!RDBMS永远不要在乎它是向前还是向后导航索引。2)您担心索引的使用,但是您的解决方案将序列附加到表中的每一行...这是一种保证不会使用适当索引的方法。
破灭

1

上面的“ Tom H”答案是正确的,它对我来说排在底部5行。

SELECT [KeyCol1], [KeyCol2], [Col3]
FROM
(SELECT TOP 5 [KeyCol1],
       [KeyCol2],
       [Col3]
  FROM [dbo].[table_name]
  ORDER BY [KeyCol1],[KeyCol2] DESC) SOME_ALAIS
  ORDER BY [KeyCol1],[KeyCol2] ASC

谢谢。


0

试试这个。

declare @floor int --this is the offset from the bottom, the number of results to exclude
declare @resultLimit int --the number of results actually retrieved for use
declare @total int --just adds them up, the total number of results fetched initially

--following is for gathering top 60 results total, then getting rid of top 50. We only keep the last 10
set @floor = 50 
set @resultLimit = 10
set @total = @floor + @resultLimit

declare @tmp0 table(
    --table body
)

declare @tmp1 table(
    --table body
)

--this line will drop the wanted results from whatever table we're selecting from
insert into @tmp0
select Top @total --what to select (the where, from, etc)

--using floor, insert the part we don't want into the second tmp table
insert into @tmp1
select top @floor * from @tmp0

--using select except, exclude top x results from the query
select * from @tmp0
except 
select * from @tmp1

是什么使您的OP代码?请添加更多有关如何解决问题的解释
-techspider

我进行了编辑。我希望它能更好地解释它,并添加更多评论。主要思想是从表中选择顶部x,然后选择顶部x-所需数量,然后使用except语句排除不需要的结果。
HumbleWebDev '16

0

我想出了一个解决方案,它不需要您知道返回的行数。

例如,如果要获取表中记录的所有位置,但最新的1个(或2个,5个或34个)除外

SELECT * 
FROM
    (SELECT ROW_NUMBER() OVER (ORDER BY CreatedDate) AS Row, * 
    FROM Locations
    WHERE UserId = 12345) AS SubQuery
WHERE Row > 1 -- or 2, or 5, or 34

0

查询一个简单的子查询将其降序排序,然后在同一列上进行升序排序就可以了。

SELECT * FROM 
    (SELECT TOP 200 * FROM [table] t2 ORDER BY t2.[column] DESC) t1
    ORDER BY t1.[column]


0

首先,使用表的原始顺序在子查询中创建索引:

ROW_NUMBER () OVER (ORDER BY (SELECT NULL) ) AS RowIndex

然后,按照RowIndex您在主查询中创建的列的顺序对表进行排序:

ORDER BY RowIndex DESC

最后使用TOP所需的行数:

    SELECT TOP 1 * --(or 2, or 5, or 34)
    FROM   (SELECT ROW_NUMBER() OVER (ORDER BY  (SELECT NULL) ) AS RowIndex, * 
            FROM MyTable) AS SubQuery
    ORDER BY RowIndex DESC
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.