Làm thế nào để chọn các hàng dưới cùng nhất?


100

Tôi có thể làm CHỌN ĐẦU (200) ... nhưng tại sao không CHỌN (200)?

Không đi sâu vào triết học, ý tôi là, làm thế nào tôi có thể làm tương đương với TOP (200) nhưng ngược lại (từ dưới lên, như bạn mong đợi BOTTOM làm ...)?

Câu trả lời:


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

2
Tại sao bạn sử dụng một bảng dẫn xuất?
RichardOD

14
Nếu bạn muốn trả lại các hàng theo thứ tự A-> Z, nhưng chọn 200 hàng đầu theo thứ tự Z-> A thì đây là một cách để thực hiện. Các câu trả lời khác, gợi ý chỉ thay đổi ORDER BY sẽ không trả lại kết quả giống như mô tả trong câu hỏi, vì chúng sẽ không theo thứ tự (trừ khi thứ tự không quan trọng, điều mà OP không nói).
Tom H

3
@Tom H. Phải suy nghĩ trong vài giây xem bạn muốn nói gì (tôi đã thức 14 giờ). Lúc đầu, tôi không thể thấy sự khác biệt giữa đơn đặt hàng của bạn và đơn đặt hàng theo câu trả lời, nhưng bây giờ tôi có thể. Vì vậy, +1.
RichardOD

1
Câu trả lời này và các câu trả lời khác hoạt động tốt khi bạn đang làm việc trên các bảng nhỏ hơn. Tôi không nghĩ rằng nó đáng để sắp xếp toàn bộ bảng theo một cột khi bạn chỉ quan tâm đến vài hàng dưới cùng.
stablefish

1
câu trả lời hay, imho tốt nhất ... vấn đề thực sự là tôi không thể làm điều này từ một tập lệnh ASP, vì vậy tôi nghĩ rằng tôi cần phải sắp xếp lại objRecordset theo cách thủ công hoặc với chức năng mà ASP cung cấp ....
Andrea_86

99

Nó là không cần thiết. Bạn có thể sử dụng một ORDER BYvà chỉ cần thay đổi cách sắp xếp DESCđể có được hiệu quả tương tự.


3
google nói điều tương tự và bây giờ 9 của bạn đồng ý, tốt đủ cho tôi, nhờ: D
CloudMeta

8
Sử dụng DESC sẽ trả về N hàng cuối cùng, nhưng các hàng được trả về cũng sẽ theo thứ tự ngược lại từ N hàng đầu tiên.
RickNZ

11
Điều gì sẽ xảy ra nếu không có chỉ mục nào trên bảng của bạn để ĐẶT HÀNG BẰNG CÁCH?
Người bảo vệ một

8
@Justin: Hãy tưởng tượng chỉ có một cột, chứa các giá trị varchar. ORDER BY sẽ sắp xếp theo thứ tự bảng chữ cái, (có thể) không phải là những gì chúng ta muốn.
Người bảo vệ một

3
Tom H. là nhà cảm xạ chính xác, nếu không, các hàng của bạn sẽ theo thứ tự ngược lại.
Pierre-Olivier Goulet

39

Xin lỗi, nhưng tôi không nghĩ rằng tôi thấy bất kỳ câu trả lời chính xác nào theo quan điểm của tôi.

Hàm TOPx hiển thị các bản ghi theo thứ tự không xác định. Từ định nghĩa đó dẫn đến một BOTTOMhàm không thể được định nghĩa.

Không phụ thuộc vào bất kỳ chỉ mục hoặc thứ tự sắp xếp nào. Khi bạn thực hiện, ORDER BY y DESCbạn sẽ nhận được các hàng có giá trị y cao nhất trước tiên. Nếu đây là ID được tạo tự động, nó sẽ hiển thị các bản ghi được thêm lần cuối vào bảng, như được đề xuất trong các câu trả lời khác. Tuy nhiên:

  • Điều này chỉ hoạt động nếu có cột id được tạo tự động
  • Nó có tác động đáng kể đến hiệu suất nếu bạn so sánh với TOPhàm

Câu trả lời đúng phải là không, và không thể, tương đương với TOPđể lấy các hàng dưới cùng.


3
Đúng, dường như không có tương đương, chỉ là cách giải quyết.
poke

4
TOP không liên quan gì đến thứ tự các mục được thêm vào bảng, nó chỉ có nghĩa là "Cung cấp bản ghi X đầu tiên phù hợp với truy vấn của tôi"
Luke

4
Có, nó cung cấp cho bạn các bản ghi đầu tiên được thêm vào bảng phù hợp với truy vấn của bạn.
Martijn Burger

4
Tôi đồng ý với Luke. Các bảng cơ sở dữ liệu theo định nghĩa không có thứ tự. Không nên dựa vào thứ tự do RDBMS cung cấp khi câu lệnh select không có mệnh đề ORDER BY. đọc ở đây trên wiki Tuy nhiên, hệ thống cơ sở dữ liệu không đảm bảo bất kỳ thứ tự nào của các hàng trừ khi mệnh đề ORDER BY được chỉ định trong câu lệnh SELECT truy vấn bảng.
Zohar Peled

2
Tôi đồng ý với câu trả lời này, nhưng trong thực tế , câu trả lời của Tom giải quyết vấn đề cho tất cả các mục đích sử dụng thực tế.
Antonio

18

Một cách hợp lý,

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

Ví dụ: Chọn dưới cùng 1000 từ Nhân viên:

Trong 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
Xin chào shadi2014, nếu không sử dụng "ORDER BY", kết quả của bạn sẽ là ngẫu nhiên.
bummi

5
bummi, bạn đúng, nhưng đó là điều khiến câu trả lời này chính xác. Về lý thuyết, Chọn TOP là "ngẫu nhiên" và đây là cách triển khai chính xác cho Chọn ĐÁY. Trong một bảng 5000 hồ sơ, dưới 1000 là tất cả mọi thứ ngoại trừ đầu 4000
tzachs

9

Có vẻ như bất kỳ câu trả lời nào triển khai mệnh đề ORDER BY trong giải pháp đều bị thiếu điểm hoặc không thực sự hiểu TOP trả về cho bạn những gì.

TOP trả về tập kết quả truy vấn không có thứ tự giới hạn tập hợp bản ghi ở N bản ghi đầu tiên được trả về. (Theo quan điểm của Oracle, nó giống với việc thêm một where ROWNUM <(N + 1).

Bất kỳ giải pháp nào sử dụng thứ tự, đều có thể trả về các hàng cũng được trả về bởi mệnh đề TOP (vì tập dữ liệu đó không có thứ tự ở vị trí đầu tiên), tùy thuộc vào tiêu chí nào được sử dụng trong thứ tự

Tính hữu ích của TOP là khi tập dữ liệu đạt đến kích thước N nhất định, nó sẽ ngừng tìm nạp các hàng. Bạn có thể cảm nhận dữ liệu trông như thế nào mà không cần phải tìm nạp tất cả.

Để triển khai BOTTOM một cách chính xác, nó sẽ cần tìm nạp toàn bộ tập dữ liệu không có thứ tự và sau đó hạn chế tập dữ liệu ở N bản ghi cuối cùng. Điều đó sẽ không đặc biệt hiệu quả nếu bạn đang xử lý các bảng lớn. Nó cũng không nhất thiết phải cung cấp cho bạn những gì bạn nghĩ bạn đang yêu cầu. Phần cuối của tập dữ liệu có thể không nhất thiết phải là "các hàng cuối cùng được chèn" (và có thể sẽ không dành cho hầu hết các ứng dụng chuyên sâu DML).

Tương tự như vậy, các giải pháp triển khai ORDER BY, thật không may, có khả năng gây tai hại khi xử lý các tập dữ liệu lớn. Chẳng hạn, nếu tôi có 10 Tỷ bản ghi và muốn có 10 bản cuối cùng, thì thật là ngu ngốc khi đặt hàng 10 Tỷ bản ghi và chọn 10 bản cuối cùng.

Vấn đề ở đây là ĐÁY không có ý nghĩa mà chúng ta nghĩ đến khi so sánh nó với TOP.

Khi các bản ghi được chèn, xóa, chèn, xóa nhiều lần, một số khoảng trống sẽ xuất hiện trong bộ nhớ và sau đó, các hàng sẽ được sắp xếp vào, nếu có thể. Nhưng những gì chúng ta thường thấy, khi chúng ta chọn TOP, dường như là dữ liệu đã được sắp xếp, bởi vì nó có thể đã được chèn từ rất sớm trong sự tồn tại của bảng. Nếu bảng không bị xóa nhiều lần, bảng có thể xuất hiện để được đặt hàng. (ví dụ: ngày tạo có thể xa hơn thời gian so với ngày tạo bảng). Nhưng thực tế là, nếu đây là một bảng nặng về tính năng xóa, thì các hàng TOP N có thể không giống như vậy chút nào.

Vì vậy, điểm mấu chốt ở đây (ý định chơi chữ) là một người nào đó đang yêu cầu các bản ghi BOTTOM N thực sự không biết họ đang yêu cầu điều gì. Hoặc, ít nhất, những gì họ yêu cầu và những gì BOTTOM thực sự có nghĩa là không giống nhau.

Vì vậy - giải pháp có thể đáp ứng nhu cầu kinh doanh thực tế của người yêu cầu ... nhưng không đáp ứng tiêu chí trở thành ĐÁY.


1
Giải thích tuyệt vời. Ủng hộ để làm sáng tỏ hơn về chủ đề này.
rohrl77

Trường hợp sử dụng của tôi là cái này. Tôi đã chạy một insertcâu lệnh lớn để đặt các hàng vào một bảng lớn, không được lập chỉ mục. (Tôi đang điền bảng trước khi bắt đầu lập chỉ mục.) Tôi đã mất phiên khách hàng của mình do khởi động lại hoặc bất cứ điều gì, và bây giờ tôi muốn xem liệu các hàng mới được thêm của tôi có ở đó hay không. Nếu hàng 'dưới cùng' của bảng là một trong những hàng gần đây của tôi, tôi biết thao tác đã hoàn tất. Nếu hàng 'dưới cùng' là thứ khác, thì không có gì đảm bảo và tôi phải quét toàn bộ bảng để chắc chắn ... nhưng rất có thể tôi có thể tiết kiệm thời gian bằng cách nhanh chóng kiểm tra 'dưới cùng' giống như bạn có thể ' hàng đầu'.
Ed Avis

Giải thích tốt, nhưng nó vẫn ngụ ý sự tồn tại của một đáy, chỉ yêu cầu dữ liệu được đọc / truy xuất ngược lại. Trong trường hợp (thừa nhận là cạnh) của một chèn bị hủy trên một bảng mới, việc xác minh bản ghi cuối cùng được chèn (dưới cùng) mà không truy xuất mọi thứ sẽ hữu ích. Có lý do kỹ thuật nào khiến dữ liệu bảng không thể được truy xuất theo thứ tự ngược lại không?
James

3

Câu trả lời hiện được chấp nhận bởi "Justin Ethier" không phải là một câu trả lời chính xác như được chỉ ra bởi "Protector one".

Theo như tôi thấy, cho đến thời điểm hiện tại, không có câu trả lời hoặc nhận xét nào khác tương đương với BOTTOM (x) mà tác giả câu hỏi yêu cầu.

Trước tiên, hãy xem xét một tình huống mà chức năng này sẽ cần thiết:

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

Điều này trả về một bảng gồm một cột và năm bản ghi:

  • táo
  • trái cam
  • trái chuối
  • táo
  • Vôi

Như bạn có thể thấy: chúng tôi không có cột ID; chúng tôi không thể đặt hàng theo cột trả về; và chúng ta không thể chọn hai bản ghi dưới cùng bằng cách sử dụng SQL tiêu chuẩn như chúng ta có thể làm cho hai bản ghi trên cùng.

Đây là nỗ lực của tôi để cung cấp giải pháp:

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

Và đây là một giải pháp hoàn chỉnh hơn:

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

Tôi hoàn toàn không khẳng định rằng đây là một ý tưởng hay để sử dụng trong mọi trường hợp, nhưng nó mang lại kết quả mong muốn.


1

Tất cả những gì bạn cần làm là đảo ngược của bạn ORDER BY. Thêm hoặc xóa DESCnó.


1

Vấn đề với việc đặt hàng theo cách khác là nó thường không tận dụng tốt các chỉ số. Nó cũng không thể mở rộng nhiều nếu bạn cần chọn một số hàng không ở đầu hoặc cuối. Một cách thay thế như sau.

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) Nếu thay đổi hướng của mệnh đề ORDER BY của bạn "không tận dụng tốt các chỉ số", thì hãy nhận một RDBMS phù hợp! RDBMS không bao giờ nên quan tâm đến việc nó điều hướng chỉ mục tiến hay lùi. 2) Bạn lo lắng về việc sử dụng các chỉ mục, nhưng giải pháp của bạn đính kèm một chuỗi vào mọi hàng trong bảng ... Đó là một cách để đảm bảo rằng một chỉ mục thích hợp sẽ KHÔNG được sử dụng.
Vỡ mộng

1

Câu trả lời "Tom H" ở trên là đúng và nó phù hợp với tôi khi nhận được 5 hàng Dưới cùng.

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

Cảm ơn.


0

thử cái này.

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

điều gì tạo nên mã của bạn cho OP? Vui lòng giải thích thêm một chút về cách bạn cố gắng giải quyết vấn đề
techspider

Tôi đã chỉnh sửa. Tôi hy vọng rằng nó giải thích nó tốt hơn một chút, thêm nhiều ý kiến. Ý tưởng chính là chọn đầu x từ một bảng, sau đó chọn x - num hàng đầu mà bạn muốn, sau đó sử dụng câu lệnh ngoại trừ để loại trừ các kết quả chưa được chọn.
HumbleWebDev

0

Tôi đã đưa ra một giải pháp cho điều này mà không yêu cầu bạn biết số hàng được trả về.

Ví dụ: nếu bạn muốn ghi tất cả các vị trí vào một bảng, ngoại trừ vị trí 1 (hoặc 2, hoặc 5, hoặc 34) mới nhất

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

Truy vấn một truy vấn con đơn giản được sắp xếp giảm dần, tiếp theo là sắp xếp trên cùng một cột tăng dần là mẹo nhỏ.

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


0

Đầu tiên, hãy tạo một chỉ mục trong một truy vấn con theo thứ tự ban đầu của bảng bằng cách sử dụng:

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

Sau đó, sắp xếp bảng giảm dần theo RowIndexcột bạn đã tạo trong truy vấn chính:

ORDER BY RowIndex DESC

Và cuối cùng sử dụng TOPvới số lượng hàng mong muốn của bạn:

    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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.