DISTINCT chỉ cho một cột


155

Hãy nói rằng tôi có truy vấn sau đây.

SELECT ID, Email, ProductName, ProductModel FROM Products

Làm thế nào tôi có thể sửa đổi nó để nó không trả lại Email trùng lặp?

Nói cách khác, khi một số hàng chứa cùng một email, tôi muốn kết quả chỉ bao gồm một trong những hàng đó (tốt nhất là hàng cuối cùng). Các bản sao trong các cột khác nên được cho phép.

Các khoản như DISTINCTGROUP BYxuất hiện để làm việc trên toàn bộ hàng. Vì vậy, tôi không chắc làm thế nào để tiếp cận điều này.


2
Ok, bạn cần sử dụng PHẦN THAM GIA hoặc sử dụng hai câu lệnh chọn?
CarneyCode

Và những gì sẽ được hiển thị nếu có 2 hàng có cùng một Email nhưng ProductName khác nhau? Các (tốt nhất là người cuối cùng) là không rõ ràng. Cuối cùng bằng cách đặt hàng?
ypercubeᵀᴹ

@ypercube Như đã nêu trong câu hỏi, tốt nhất là câu cuối cùng. Tuy nhiên, điều đó không thực sự quan trọng với tôi. Tôi chỉ muốn một trong số họ.
Jonathan Wood

1
Bạn có thể xem các câu hỏi sau: question1 , question2 hoặc question3 .
Mary

Tại sao bạn không thể sử dụng: CHỌN DISTINCT Email, ID, ProductName, ProductModel TỪ sản phẩm?
Rick Henderson

Câu trả lời:


186

Nếu bạn đang sử dụng SQL Server 2005 trở lên, hãy sử dụng:

SELECT *
  FROM (
                SELECT  ID, 
                        Email, 
                        ProductName, 
                        ProductModel,
                        ROW_NUMBER() OVER(PARTITION BY Email ORDER BY ID DESC) rn
                    FROM Products
              ) a
WHERE rn = 1

EDIT: Ví dụ sử dụng mệnh đề where:

SELECT *
  FROM (
                SELECT  ID, 
                        Email, 
                        ProductName, 
                        ProductModel,
                        ROW_NUMBER() OVER(PARTITION BY Email ORDER BY ID DESC) rn
                    FROM Products
                   WHERE ProductModel = 2
                     AND ProductName LIKE 'CYBER%'

              ) a
WHERE rn = 1

4
Tôi phải điều tra điều khoản THAM GIA này, chưa bao giờ thấy nó hoạt động trước đây. Cảm ơn ví dụ
LorenVS

@Cybernate Một biến chứng: Nội tâm của tôi SELECTcần một WHEREđiều kiện. Tôi nghĩ rằng số hàng sẽ được gán cho tất cả các hàng trong bảng. Cú pháp này chỉ là một chút ngoài tôi. Bất kỳ cơ hội cập nhật nào sẽ đảm bảo một hàng với một email cụ thể đáp ứng WHEREđiều kiện?
Jonathan Wood

1
Bạn có thể thêm mệnh đề where vào sql bên trong. Tôi sẽ cập nhật bài đăng một khi tôi có thể truy cập máy tính xách tay của mình
Chandu

1
Cập nhật bài viết với một mẫu sử dụng mệnh đề where.
Chandu

1
Tôi nhận được điều này chỉ hoạt động chính xác khi không có JOIN s trong truy vấn của tôi. Ngay khi tôi có a JOIN, ROW_NUMBERgiá trị trả về cao hơn nhiều so với "1".
Uwe Keim

10

Điều này giả định SQL Server 2005+ và định nghĩa "cuối cùng" của bạn là PK tối đa cho một email nhất định

WITH CTE AS
(
SELECT ID, 
       Email, 
       ProductName, 
       ProductModel, 
       ROW_NUMBER() OVER (PARTITION BY Email ORDER BY ID DESC) AS RowNumber 
FROM   Products
)
SELECT ID, 
       Email, 
       ProductName, 
       ProductModel
FROM CTE 
WHERE RowNumber = 1

6

Khi bạn sử dụng DISTINCThãy nghĩ về nó như một hàng riêng biệt, không phải cột. Nó sẽ chỉ trả về các hàng trong đó các cột không khớp chính xác như nhau.

SELECT DISTINCT ID, Email, ProductName, ProductModel
FROM Products

----------------------
1 | something@something.com | ProductName1 | ProductModel1
2 | something@something.com | ProductName1 | ProductModel1

Truy vấn sẽ trả về cả hai hàng vì IDcột khác nhau. Tôi giả sử rằng IDcột là một IDENTITYcột đang tăng lên, nếu bạn muốn trả về cuối cùng thì tôi khuyên bạn nên một cái gì đó như thế này:

SELECT DISTINCT TOP 1 ID, Email, ProductName, ProductModel
FROM Products
ORDER BY ID DESC

Các TOP 1sẽ trở lại chỉ có hồ sơ đầu tiên, bằng cách đặt hàng nó bằng IDgiảm dần nó sẽ trả lại kết quả với hàng cuối cùng đầu tiên. Điều này sẽ cung cấp cho bạn hồ sơ cuối cùng.


2
Như đã nêu trong câu hỏi, tôi thấy rằng DISTINCT hoạt động trên toàn bộ hàng. Tôi muốn làm như bạn đề xuất ở trên, nhưng mỗi lần email được nhân đôi trong kết quả (không chỉ một lần).
Jonathan Wood

Trong trường hợp đó, tôi khuyên bạn nên đi với câu trả lời @Cybernate. Điều đó sẽ làm chính xác những gì bạn cần.
jon3laze

4

Bạn có thể vượt qua điều đó bằng cách sử dụng chức năng GROUP BY

SELECT ID, Email, ProductName, ProductModel FROM Products GROUP BY Email


16
Cột 'Products.ID' không hợp lệ trong danh sách chọn vì nó không có trong hàm tổng hợp hoặc mệnh đề GROUP BY.
palota

2
Điều này không hoạt động mà không sử dụng cái gì đó như MAX (ID), MAX (Tên sản phẩm), MAX (ProductModel) cho các cột khác
avl_sweden

2
Trong postgres, bạn chỉ cần hàm tổng hợp trên cột sẽ được sử dụng trong nhóm theo mệnh đề, vd SELECT id, max(email) AS email FROM tbl GROUP by email. Trong máy chủ SQL TẤT CẢ các cột trong SELECTmệnh đề phải nằm trong hàm tổng hợp. Điều này cắn tôi mỗi khi tôi trở lại.
Bruce Pierson

Điều này sẽ không bao giờ làm việc. Đó là một giải pháp tồi
Dan AS

1

Đối với Access, bạn có thể sử dụng truy vấn SQL Chọn mà tôi trình bày ở đây:

Ví dụ: bạn có bảng này:

KHÁCH HÀNG | | NOMBRES | | THƯ

888 || KIẾM T800 | | t800.arnold@cyberdyne.com

123 || CÔNG VIỆC KẾT NỐI | | s.connor@skynet.com

125 || KẾT NỐI SARAH ||s.connor@skynet.com

Và bạn chỉ cần chọn các thư riêng biệt. Bạn có thể làm điều này với điều này:

CHỌN SQL:

SELECT MAX(p.CLIENTE) AS ID_CLIENTE
, (SELECT TOP 1 x.NOMBRES 
    FROM Rep_Pre_Ene_MUESTRA AS x 
    WHERE x.MAIL=p.MAIL 
     AND x.CLIENTE=(SELECT MAX(l.CLIENTE) FROM Rep_Pre_Ene_MUESTRA AS l WHERE x.MAIL=l.MAIL)) AS NOMBRE, 
p.MAIL
FROM Rep_Pre_Ene_MUESTRA AS p
GROUP BY p.MAIL;

Bạn có thể sử dụng điều này để chọn ID tối đa, tên tương ứng cho ID tối đa đó, bạn có thể thêm bất kỳ thuộc tính nào khác theo cách đó. Sau đó, ở cuối, bạn đặt cột riêng biệt để lọc và bạn chỉ nhóm nó với cột riêng biệt cuối cùng đó.

Điều này sẽ mang lại cho bạn ID tối đa với dữ liệu tương ứng, bạn có thể sử dụng min hoặc bất kỳ chức năng nào khác và bạn sao chép chức năng đó sang các truy vấn phụ.

Lựa chọn này sẽ trở lại:

KHÁCH HÀNG | | NOMBRES | | THƯ

888 || KIẾM T800 | | t800.arnold@cyberdyne.com

125 || KẾT NỐI SARAH ||s.connor@skynet.com

Hãy nhớ lập chỉ mục các cột bạn chọn và cột riêng biệt phải không có dữ liệu số ở dạng chữ hoa hoặc chữ thường, nếu không nó sẽ không hoạt động. Điều này sẽ làm việc với chỉ một thư đăng ký là tốt. Chúc mừng mã hóa !!!


0

Lý do DISTINCTGROUP BYcông việc trên toàn bộ các hàng là truy vấn của bạn trả về toàn bộ các hàng.

Để giúp bạn hiểu: Hãy thử viết bằng tay những gì truy vấn sẽ trả về và bạn sẽ thấy rằng đó là mơ hồ những gì cần đặt trong các cột không trùng lặp.

Nếu bạn thực sự không quan tâm những gì trong các cột khác, đừng trả lại chúng. Trả lại một hàng ngẫu nhiên cho mỗi địa chỉ email có vẻ hơi vô dụng đối với tôi.


@JohnFix Tôi muốn trả lại toàn bộ hàng. Tôi chỉ không muốn các hàng được trả về khi kết quả đã bao gồm một hàng có cùng giá trị trong cột Email.
Jonathan Wood

Vì vậy, làm thế nào nó nên quyết định trở lại? Bạn có thực sự muốn một truy vấn trả về một hàng tùy ý cho mỗi e-mail. Điều này thực sự có mùi như bạn có thể cần phải suy nghĩ lại vấn đề bạn đang cố gắng giải quyết. Hầu như mỗi lần tôi được hỏi câu hỏi này (và nó xuất hiện rất nhiều) hóa ra nhà phát triển đã không nghĩ đến hậu quả trong ứng dụng cho hành vi này.
JohnFx

6
Tôi thực sự gặp khó khăn khi theo logic của bạn. Như đã nêu trong câu hỏi, tôi thích cái cuối cùng (được sắp xếp theo ID). Có, nếu nó chọn một hàng ngẫu nhiên sẽ ổn. Và, vâng, tôi đã nghĩ về nó.
Jonathan Wood

0

Thử cái này

;With Tab AS (SELECT DISTINCT Email FROM  Products)
SELECT Email,ROW_NUMBER() OVER(ORDER BY Email ASC) AS  Id FROM Tab
ORDER BY Email ASC

-2

Thử cái này:

SELECT ID, Email, ProductName, ProductModel FROM Products WHERE ID IN (SELECT MAX(ID) FROM Products GROUP BY Email)

2
Tại sao chúng ta nên thử điều này? Tại sao điều này tốt hơn các câu trả lời khác được đăng ở đây trong 8 năm qua? Nếu bạn muốn chia sẻ một cách tốt hơn để giải quyết vấn đề, bạn cần giải thích lý do tại sao bạn đề xuất nó.
Dharman
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.