Làm thế nào để sử dụng DISTINCT và ORDER BY trong cùng một câu lệnh SELECT?


116

Sau khi thực hiện câu lệnh sau:

SELECT  Category  FROM MonitoringJob ORDER BY CreationDate DESC

Tôi nhận được các giá trị sau từ cơ sở dữ liệu:

test3
test3
bildung
test4
test3
test2
test1

nhưng tôi muốn các bản sao bị xóa, như thế này:

bildung
test4
test3
test2
test1

Tôi đã cố gắng sử dụng DISTINCT nhưng nó không hoạt động với ORDER BY trong một câu lệnh. Xin vui lòng giúp đỡ.

Quan trọng:

  1. Tôi đã thử nó với:

    SELECT DISTINCT Category FROM MonitoringJob ORDER BY CreationDate DESC

    nó không hoạt động.

  2. Đặt hàng theo Ngày tạo là rất quan trọng.


1
Làm thế nào để nó không hoạt động? Đầu ra sai?
Fedearne

Câu trả lời:


194

Vấn đề là các cột được sử dụng trong ORDER BYkhông được chỉ định trong DISTINCT. Để làm điều này, bạn cần sử dụng một hàm tổng hợp để sắp xếp và sử dụng một GROUP BYđể thực hiện DISTINCTcông việc.

Hãy thử một cái gì đó như sau:

SELECT DISTINCT Category, MAX(CreationDate) 
FROM MonitoringJob 
GROUP BY Category 
ORDER BY MAX(CreationDate) DESC, Category

98
Bạn thậm chí không cần từ khóa DISTINCT nếu bạn đang nhóm theo Danh mục.
MatBailie

18

Các cột khóa sắp xếp mở rộng

Lý do tại sao những gì bạn muốn làm không hoạt động là do thứ tự logic của các hoạt động trong SQL , đối với truy vấn đầu tiên của bạn, là (đơn giản hóa):

  • FROM MonitoringJob
  • SELECT Category, CreationDatetức là thêm một cột được gọi là khóa sắp xếp mở rộng
  • ORDER BY CreationDate DESC
  • SELECT Categorytức là loại bỏ cột khóa sắp xếp mở rộng một lần nữa khỏi kết quả.

Vì vậy, nhờ tính năng cột khóa sắp xếp mở rộng tiêu chuẩn SQL , bạn hoàn toàn có thể sắp xếp thứ tự theo thứ gì đó không có trong SELECTmệnh đề, bởi vì nó tạm thời được thêm vào nó ở phía sau.

Vì vậy, tại sao điều này không hoạt động với DISTINCT?

Nếu chúng tôi thêm DISTINCTthao tác, nó sẽ được thêm vào giữa SELECTORDER BY:

  • FROM MonitoringJob
  • SELECT Category, CreationDate
  • DISTINCT
  • ORDER BY CreationDate DESC
  • SELECT Category

Nhưng bây giờ, với cột khóa sắp xếp mở rộng CreationDate , ngữ nghĩa của DISTINCTthao tác đã được thay đổi, do đó kết quả sẽ không còn như cũ. Đây không phải là những gì chúng tôi muốn, vì vậy cả tiêu chuẩn SQL và tất cả các cơ sở dữ liệu hợp lý đều cấm việc sử dụng này.

Cách giải quyết

Nó có thể được mô phỏng với cú pháp tiêu chuẩn như sau

SELECT Category
FROM (
  SELECT Category, MAX(CreationDate) AS CreationDate
  FROM MonitoringJob
  GROUP BY Category
) t
ORDER BY CreationDate DESC

Hoặc, chỉ đơn giản (trong trường hợp này), như được hiển thị bởi Prutswonder

SELECT Category, MAX(CreationDate) AS CreationDate
FROM MonitoringJob
GROUP BY Category
ORDER BY CreationDate DESC

Tôi đã viết blog về SQL DISTINCT và ORDER BY chi tiết hơn ở đây .


1
Tôi nghĩ rằng bạn đang nhầm với cách DISTINCT ONhoạt động và khá chắc chắn rằng nó không giúp ích gì ở đây. Biểu thức trong ngoặc đơn được sử dụng để xác định tính khác biệt (điều kiện phân nhóm). Nếu có các danh mục khác nhau có cùng danh mục CreationDatethì chỉ một trong số chúng sẽ xuất hiện trong kết quả! Vì tôi đang tự hỏi liệu có thể tôi đã sai bằng cách nào đó hay không, tôi cũng đã tải cơ sở dữ liệu mẫu trong bài đăng blog của bạn để kiểm tra kỹ: DISTINCT ONtruy vấn bạn đưa ra ở đó tạo ra tổng cộng 1000 kết quả (với nhiều bản sao length) trong khi truy vấn bên dưới nó cho chỉ có 140 giá trị (duy nhất).
Inkling

@Inkling: Cảm ơn bạn đã dành thời gian. OP muốn xóa "bản sao" một cách rõ ràng. Xem từ ngữ của OP "nhưng tôi muốn các bản sao bị xóa, như thế này" . Có thể bạn đã mắc lỗi khi sao chép các truy vấn từ bài đăng trên blog của tôi. Có hai truy vấn, một truy vấn sử dụng DISTINCT(không ON) và một truy vấn sử dụng DISTINCT ON. Vui lòng lưu ý rằng sau này rõ ràng không loại bỏ các độ dài trùng lặp, nhưng các tiêu đề trùng lặp. Tôi nghĩ rằng câu trả lời của tôi ở đây là hoàn toàn chính xác.
Lukas Eder

1
Ý của tôi là các DISTINCT ONđiều kiện của bạn đang xóa các bản sao bằng cách sử dụng điều kiện sai. Trong bài đăng trên blog của bạn, DISTINCT ONtruy vấn thực sự loại bỏ các tiêu đề trùng lặp , tuy nhiên DISTINCTtruy vấn phía trên nó và truy vấn bên dưới nó (mà bạn cho rằng nó là "đường cú pháp") đều xóa các độ dài trùng lặp , vì đó có lẽ là toàn bộ mục tiêu. Điều tương tự cũng áp dụng ở đây: OP muốn loại bỏ các Danh mục trùng lặp , không phải các Ngày tạo trùng lặp như DISTINCT ONtruy vấn. Nếu bạn vẫn chưa tin tôi, hãy tự mình kiểm chứng.
Inkling

6

Nếu đầu ra của MAX (Ngày tạo) không được mong muốn - như trong ví dụ của câu hỏi ban đầu - câu trả lời duy nhất là câu trả lời thứ hai của câu trả lời Prashant Gupta:

SELECT [Category] FROM [MonitoringJob] 
GROUP BY [Category] ORDER BY MAX([CreationDate]) DESC

Giải thích: bạn không thể sử dụng mệnh đề ORDER BY trong một hàm nội tuyến, vì vậy câu lệnh trong câu trả lời của Prutswonder không thể sử dụng được trong trường hợp này, bạn không thể đặt một vùng chọn bên ngoài xung quanh nó và loại bỏ phần MAX (CreationDate).


2

Chỉ cần sử dụng mã này, nếu bạn muốn các giá trị của cột [Category] và [CreationDate]

SELECT [Category], MAX([CreationDate]) FROM [MonitoringJob] 
             GROUP BY [Category] ORDER BY MAX([CreationDate]) DESC

Hoặc sử dụng mã này, Nếu bạn chỉ muốn các giá trị của cột [Category].

SELECT [Category] FROM [MonitoringJob] 
GROUP BY [Category] ORDER BY MAX([CreationDate]) DESC

Bạn sẽ có tất cả các bản ghi riêng biệt mà bạn muốn.


những dấu ngoặc nhọn [] hoàn toàn khó hiểu ... đây có phải là cú pháp SQL hợp lệ không?
m13r

1
Các dấu ngoặc dành cho các từ khóa thoát, chẳng hạn như Thứ tự, sự kiện, v.v., vì vậy nếu bạn có (ví dụ) một cột trong bảng của mình được gọi, Eventbạn có thể viết [Event]thay vì Eventđể ngăn SQL phát ra lỗi phân tích cú pháp.
Ben Maxfield

1

2) Đặt hàng theo Ngày tạo là rất quan trọng

Kết quả ban đầu chỉ ra rằng "test3" có nhiều kết quả ...

Rất dễ dàng bắt đầu sử dụng MAX mọi lúc để xóa các bản sao trong Group By ... và quên hoặc bỏ qua câu hỏi cơ bản là gì ...

OP có lẽ đã nhận ra rằng việc sử dụng MAX mang lại cho anh ta "được tạo" cuối cùng và sử dụng MIN sẽ cung cấp cho "được tạo" đầu tiên ...


3
Điều này dường như không thực sự trả lời câu hỏi, nó dường như là một nhận xét về cách sử dụng của người trả lời khác MAX, chứ không phải là một cái gì đó độc lập như một câu trả lời cho câu hỏi.
DaveyDaveDave

0
if object_id ('tempdb..#tempreport') is not null
begin  
drop table #tempreport
end 
create table #tempreport (
Category  nvarchar(510),
CreationDate smallint )
insert into #tempreport 
select distinct Category from MonitoringJob (nolock) 
select * from #tempreport  ORDER BY CreationDate DESC

0

Theo truy vấn con, nó sẽ hoạt động:

    SELECT distinct(Category) from MonitoringJob  where Category in(select Category from MonitoringJob order by CreationDate desc);

Ummm ... Tôi không nghĩ nó sẽ như vậy. Lựa chọn bên ngoài không được sắp xếp.
Hossam El-Deen

nó sẽ không hoạt động, tôi ở đây vì điều này không hoạt động
Amirreza

-1

Distinction sẽ sắp xếp các bản ghi theo thứ tự tăng dần. Nếu bạn muốn sắp xếp theo thứ tự mô tả, hãy sử dụng:

SELECT DISTINCT Category
FROM MonitoringJob
ORDER BY Category DESC

Nếu bạn muốn sắp xếp các bản ghi dựa trên trường CreationDate thì trường này phải nằm trong câu lệnh select:

SELECT DISTINCT Category, creationDate
FROM MonitoringJob
ORDER BY CreationDate DESC

12
Điều này sẽ thực thi nhưng sẽ không cung cấp những gì OP cần. OP muốn các Danh mục riêng biệt, không phải kết hợp riêng biệt của Danh mục và Ngày tạo. Mã này có thể mang lại một số bản sao của cùng một Danh mục, mỗi bản có các giá trị Ngày tạo khác nhau.
MatBailie

-1

Bạn có thể sử dụng CTE:

WITH DistinctMonitoringJob AS (
    SELECT DISTINCT Category Distinct_Category FROM MonitoringJob 
)

SELECT Distinct_Category 
FROM DistinctMonitoringJob 
ORDER BY Distinct_Category DESC

-3

Hãy thử tiếp theo, nhưng nó không hữu ích cho dữ liệu lớn ...

SELECT DISTINCT Cat FROM (
  SELECT Category as Cat FROM MonitoringJob ORDER BY CreationDate DESC
);

4
"Mệnh đề ORDER BY không hợp lệ trong các dạng xem, hàm nội tuyến, bảng dẫn xuất, truy vấn con và biểu thức bảng thông thường, trừ khi TOP hoặc FOR XML cũng được chỉ định."
TechplexEngineer

Điều này không hoạt động bởi vì bạn không chỉ định cột Ngày tạo theo thứ tự.
Mauro Bilotti

1
@TechplexEngineer Nhận xét của bạn không chính xác. Sử dụng ORDER BYtrong các truy vấn phụ là hoàn toàn hợp lệ. Và ai đó thậm chí đã bỏ phiếu cho nhận xét không chính xác của bạn.
Racil Hilan,

Tôi đang thử điều này và gặp lỗi tương tự với @TechplexEngineer. Tôi đang sử dụng đặt hàng tùy chỉnh với trường hợp khi nào.
Ege Bayrak

-4

Nó có thể được thực hiện bằng cách sử dụng truy vấn bên trong Như thế này

$query = "SELECT * 
            FROM (SELECT Category  
                FROM currency_rates                 
                ORDER BY id DESC) as rows               
            GROUP BY currency";

-5
SELECT DISTINCT Category FROM MonitoringJob ORDER BY Category ASC

2
tôi cần nó được sắp xếp theo ngày tạo !! nó rất quan trọng
rr

Như vậy là không thể tự mình thêm cột muốn đặt? Ví dụ của bạn cho thấy các mục nhập được sắp xếp theo thứ tự bảng chữ cái. Nếu bạn cần đặt hàng theo ngày tạo, chỉ cần thêm nó. Nó thực sự không khó.
Furicane

8
-1: OP đã thử điều đó, nó không hoạt động, bởi vì điều đó là không thể và bạn dường như đã bỏ qua sự thật đó khi bảo trợ OP. Vấn đề là toán tử DISTINCT sẽ đối chiếu một số bản ghi có cùng giá trị Danh mục, mỗi bản ghi có ngày tạo khác nhau. Vì vậy, về mặt logic là không thể khi sử dụng DISTINCT. Điều này đẩy logic được yêu cầu thành GROUP BY thay vì DISTINCT, cho phép tổng hợp (MAX) vào ngày tạo.
MatBailie

Trên thực tế, nếu bạn xem xét kỹ hơn những gì OP đã làm, đó hoàn toàn là SQL không đúng định dạng - tôi đã không mắc một lỗi nào và kết quả đưa ra tương ứng với kết quả mà anh ấy yêu cầu. Tôi sẽ không bận tâm đến -1, chỉ cần đọc lần sau trước khi sửa chữa mọi người. Cảm ơn bạn.
Furicane

8
Bạn trực tiếp đề xuất thêm trường CreationDate, thậm chí nói rằng "nó thực sự không khó lắm". Làm như vậy tạo ra SQL không đúng định dạng. Bạn nhận được -1 vì bảo trợ OP, đưa ra lời khuyên đưa OP trở lại câu lệnh mà anh ấy đã đăng ban đầu và không nhận thấy sự tranh chấp giữa DISTINCT và việc sắp xếp theo trường không có trong DISTINCT. Ngoài ra, 'b' đứng trước 't' và '1' đứng trước '4', do đó các kết quả do OP đưa ra được phân loại không theo thứ tự bảng chữ cái. Tôi có thể đề nghị lời khuyên của riêng bạn sau đó: đọc (cẩn thận hơn) vào lần sau.
MatBailie
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.