Cách chọn bản ghi duy nhất bằng SQL


87

Khi tôi thực hiện "SELECT * FROM table", tôi nhận được kết quả như sau:

1 item1 data1
2 item1 data2
3 item2 data3
4 item3 data4

Như bạn có thể thấy, có các bản ghi trùng lặp từ cột 2 (mục 1 bị chèn). Vì vậy, làm thế nào tôi có thể nhận được kết quả như thế này:

1 item1 data1
2 item2 data3
3 item3 data4

Chỉ một bản ghi được trả về từ bản sao, cùng với phần còn lại của các bản ghi duy nhất.


Mục 1 không bị trùng lặp về mặt kỹ thuật. Như được hiển thị, hàng 1 và 2 là các quan sát duy nhất. Điều gì sẽ xảy ra nếu bạn muốn giữ hàng 2 chứ không phải hàng 1?
Cybernetic

Câu trả lời:


105

Với distincttừ khóa có tên cột đơn và nhiều cột, bạn sẽ có các bản ghi riêng biệt:

SELECT DISTINCT column 1, column 2, ...
FROM table_name;

14
Nó có thể được rằng câu trả lời thực sự là sai? DISTINCT được áp dụng cho tất cả các cột đã chọn (ít nhất là trên DB2), nó vẫn sẽ trả về các giá trị trùng lặp trong các cột riêng lẻ.
Konstantin

26

Nếu bạn chỉ cần loại bỏ các bản sao thì hãy sử dụng DISTINCT. GROUP BYnên được sử dụng để áp dụng các toán tử tổng hợp cho mỗi nhóm

GROUP BY v DISTINCT


11

Nó phụ thuộc vào dòng mà bạn muốn trả lại cho mỗi mục duy nhất. Dữ liệu của bạn dường như chỉ ra giá trị dữ liệu tối thiểu nên trong trường hợp này đối với SQL Server.

SELECT item, min(data)
FROM  table
GROUP BY item

10

Có 4 phương pháp bạn có thể sử dụng:

  1. DISTINCT
  2. NHÓM THEO
  3. Truy vấn con
  4. Biểu thức bảng chung (CTE) với ROW_NUMBER ()

Hãy xem xét mẫu sau TABLEvới dữ liệu thử nghiệm:

/** Create test table */
CREATE TEMPORARY TABLE dupes(word text, num int, id int);

/** Add test data with duplicates */
INSERT INTO dupes(word, num, id)
VALUES ('aaa', 100, 1)
      ,('bbb', 200, 2)
      ,('ccc', 300, 3)
      ,('bbb', 400, 4)
      ,('bbb', 200, 5)     -- duplicate
      ,('ccc', 300, 6)     -- duplicate
      ,('ddd', 400, 7)
      ,('bbb', 400, 8)     -- duplicate
      ,('aaa', 100, 9)     -- duplicate
      ,('ccc', 300, 10);   -- duplicate

Tùy chọn 1: CHỌN DISTINCT

Đây là cách đơn giản và dễ hiểu nhất, nhưng cũng là cách hạn chế nhất:

SELECT DISTINCT word, num 
FROM    dupes
ORDER BY word, num;

/*
word|num|
----|---|
aaa |100|
bbb |200|
bbb |400|
ccc |300|
ddd |400|
*/

Tùy chọn 2: NHÓM THEO

Nhóm cho phép bạn thêm dữ liệu tổng hợp, giống như min(id), max(id), count(*), vv:

SELECT  word, num, min(id), max(id), count(*)
FROM    dupes
GROUP BY word, num
ORDER BY word, num;

/*
word|num|min|max|count|
----|---|---|---|-----|
aaa |100|  1|  9|    2|
bbb |200|  2|  5|    2|
bbb |400|  4|  8|    2|
ccc |300|  3| 10|    3|
ddd |400|  7|  7|    1|
*/

Tùy chọn 3: Truy vấn con

Sử dụng truy vấn con, trước tiên bạn có thể xác định các hàng trùng lặp để bỏ qua, sau đó lọc chúng ra trong truy vấn bên ngoài với WHERE NOT IN (subquery)cấu trúc:

/** Find the higher id values of duplicates, distinct only added for clarity */
    SELECT  distinct d2.id
    FROM    dupes d1
        INNER JOIN dupes d2 ON d2.word=d1.word AND d2.num=d1.num
    WHERE d2.id > d1.id

/*
id|
--|
 5|
 6|
 8|
 9|
10|
*/

/** Use the previous query in a subquery to exclude the dupliates with higher id values */
SELECT  *
FROM    dupes
WHERE   id NOT IN (
    SELECT  d2.id
    FROM    dupes d1
        INNER JOIN dupes d2 ON d2.word=d1.word AND d2.num=d1.num
    WHERE d2.id > d1.id
)
ORDER BY word, num;

/*
word|num|id|
----|---|--|
aaa |100| 1|
bbb |200| 2|
bbb |400| 4|
ccc |300| 3|
ddd |400| 7|
*/

Tùy chọn 4: Biểu thức bảng chung với ROW_NUMBER ()

Trong Biểu thức Bảng Chung (CTE), chọn ROW_NUMBER (), được phân vùng theo cột nhóm và sắp xếp theo thứ tự mong muốn. Sau đó CHỌN chỉ các bản ghi có ROW_NUMBER() = 1:

WITH CTE AS (
    SELECT  *
           ,row_number() OVER(PARTITION BY word, num ORDER BY id) AS row_num
    FROM    dupes
)
SELECT  word, num, id 
FROM    cte
WHERE   row_num = 1
ORDER BY word, num;

/*
word|num|id|
----|---|--|
aaa |100| 1|
bbb |200| 2|
bbb |400| 4|
ccc |300| 3|
ddd |400| 7|
*/

6

chỉ sử dụng liên kết bên trong vì nhóm theo sẽ không hoạt động với nhiều cột nói rằng không có trong một hàm tổng hợp.

SELECT a.*
FROM yourtable a
INNER JOIN 
  (SELECT yourcolumn,
    MIN(id) as id
  FROM yourtable 
  GROUP BY yourcolumn
) AS b
  ON a.yourcolumn= b.yourcolumn
  AND a.id = b.id;

Đó là câu trả lời cho một câu hỏi khác nhau, có lẽ là một cần được gắn thẻ với greatest-n-per-group
a_horse_with_no_name

Đây và lời giải của Dave Baker là lời giải chính xác cho câu hỏi SO. Ưu điểm của giải pháp này là nó cho phép chọn các hàng chỉ có một số cột riêng biệt được chỉ định và một cột MIN (id) AS id phải được xác định để chỉ chọn một trong nhiều cột được chỉ định.
giordano

1

Tôi thấy rằng nếu tôi không thể sử dụng DISTINCT vì bất kỳ lý do gì, thì GROUP BY sẽ hoạt động.


1

Để nhận được tất cả các cột trong kết quả của bạn, bạn cần đặt một cái gì đó như:

SELECT distinct a, Table.* FROM Table

nó sẽ đặt a làm cột đầu tiên và phần còn lại sẽ là TẤT CẢ các cột theo thứ tự như định nghĩa của bạn. Đây là, cột a sẽ được lặp lại.


1
Bạn có chắc về điều này? Tôi cố gắng này trên w3schools và nó trở lại giống như SELECT *, ngoại trừ một là cột đầu tiên
quái

@Freakishly có và đó là chính xác những gì nói nó sẽ làm trong câu trả lời của tôi: /
htafoya

Công việc sẽ không này, bạn không thể chọn * sau khi phân biệt như thế (bạn sẽ nhận được một lỗi 1064 - Lỗi trong cú pháp SQL của bạn)
tim.baker

@Mohsinkhan à, tôi đã quên đặt bạn cần viết tên bảng. Bằng cách nào đó khi tôi viết điều này nó làm việc nhưng tôi chỉ kiểm tra ngay bây giờ và nó đã làm không phải không có các tên bảng trước *
htafoya

2
Điều này giống hệt nhưselect distinct * from ...
a_horse_with_no_name.

-4

Chọn Eff_st từ (chọn EFF_ST, ROW_NUMBER () trên (PARTITION BY eff_st) XYZ - từ ABC.CODE_DIM

) trong đó XYZ = 1 đơn đặt hàng theo EFF_ST chỉ tìm nạp 5 hàng đầu tiên

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.