Tìm giá trị trùng lặp trong MySQL


769

Tôi có một bảng với một cột varchar và tôi muốn tìm tất cả các bản ghi có giá trị trùng lặp trong cột này. Truy vấn tốt nhất tôi có thể sử dụng để tìm các bản sao là gì?


1
Vì bạn đã đề cập tìm tất cả các bản ghi, tôi giả sử bạn cần biết các KHÓA cũng như các GIÁ TRỊ trùng lặp trong cột varchar đó.
TechTravelThink

Tôi có thể tìm thấy các khóa đủ dễ dàng sau khi tôi nhận được các giá trị, tôi thực sự chỉ muốn một danh sách tất cả các giá trị trùng lặp.
Jon Tackabury

Câu trả lời:


1522

Làm một SELECTvới một GROUP BYmệnh đề. Giả sử tên là cột bạn muốn tìm bản sao trong:

SELECT name, COUNT(*) c FROM table GROUP BY name HAVING c > 1;

Điều này sẽ trả về một kết quả với giá trị tên trong cột đầu tiên và đếm số lần giá trị đó xuất hiện trong lần thứ hai.


27
Nhưng làm thế nào là hữu ích nếu bạn không thể nhận được ID của các hàng có giá trị trùng lặp? Có, bạn có thể thực hiện một truy vấn mới phù hợp với từng giá trị trùng lặp, nhưng có thể chỉ liệt kê các mục trùng lặp không?
NobleUplift

23
@NobleUplift Bạn có thể làm một GROUP_CONCAT(id)và nó sẽ liệt kê các ID. Xem câu trả lời của tôi cho một ví dụ.
Matt Rardon

5
Nó có nghĩa là gì nếu nó nói ERROR: column "c" does not exist LINE 1?
Người dùng

15
Tôi bối rối tại sao đây là câu trả lời được chấp nhận và tại sao nó có quá nhiều sự ủng hộ. OP đã hỏi: "Tôi muốn tìm tất cả các bản ghi có giá trị trùng lặp trong cột này." Câu trả lời này trả về một bảng đếm. -1
Heddeck

4
Đối với những người không hiểu cách thức hoạt động của HAVING - nó chỉ đơn giản là một bộ lọc trên tập kết quả, do đó sẽ xảy ra sau truy vấn chính.
John Hunt

236
SELECT varchar_col
FROM table
GROUP BY varchar_col
HAVING COUNT(*) > 1;

10
Cao hơn câu trả lời của @ levik vì nó không thêm một cột phụ. Làm cho nó hữu ích để sử dụng với IN()/ NOT IN().
wmassingham

172
SELECT  *
FROM    mytable mto
WHERE   EXISTS
        (
        SELECT  1
        FROM    mytable mti
        WHERE   mti.varchar_column = mto.varchar_column
        LIMIT 1, 1
        )

Truy vấn này trả về các bản ghi đầy đủ, không chỉ riêng biệt varchar_column.

Truy vấn này không sử dụng COUNT(*). Nếu có nhiều bản sao, COUNT(*)đắt tiền và bạn không cần toàn bộ COUNT(*), bạn chỉ cần biết nếu có hai hàng có cùng giá trị.

Tất nhiên, có một chỉ mục theo varchar_columný muốn, tăng tốc truy vấn này rất nhiều.


3
Rất tốt. Tôi đã thêm vào ORDER BY varchar_column DESCcuối truy vấn.
trante

8
Đây phải là câu trả lời được chấp nhận, GROUP BYHAVINGchỉ trả về một trong những trùng lặp có thể có. Ngoài ra, hiệu suất với trường được lập chỉ mục thay vì COUNT(*)và khả năng ORDER BYnhóm các bản ghi trùng lặp.
Rémi Breton

1
Như đã nêu trong các ý kiến ​​trên, truy vấn này cho phép bạn liệt kê tất cả các hàng trùng lặp. Rất hữu ích.
TryHarder

4
Nhìn vào điều này tôi không hiểu nó sẽ hoạt động như thế nào. Điều kiện bên trong sẽ luôn luôn đúng vì bất kỳ hàng nào trong bảng bên ngoài cũng sẽ có sẵn trong bảng bên trong và vì vậy mọi hàng sẽ luôn luôn ít nhất khớp với chính nó? Tôi đã thử truy vấn và nhận được kết quả mà tôi nghi ngờ - mỗi hàng trả về. Nhưng với rất nhiều sự ủng hộ, tôi nghi ngờ chính mình. Không phải là truy vấn bên trong thiếu một cái gì đó như "VÀ mto.id <> mti.id"? Nó làm việc cho tôi khi tôi thêm nó.
Clox

2
@Quassnoi Được rồi. Tôi đã thử đưa nó lên sqlfiddle nhưng tôi đã từ bỏ vì mọi truy vấn tôi cố gắng chạy, ngoài việc tạo lược đồ sẽ hết thời gian. Tôi đã nhận ra rằng chỉ cần loại bỏ "EXISTS" cũng làm cho truy vấn hoạt động chính xác với tôi.
Clox

144

Dựa trên câu trả lời của levik để lấy ID của các hàng trùng lặp, bạn có thể thực hiện GROUP_CONCATnếu máy chủ của bạn hỗ trợ (điều này sẽ trả về danh sách id được phân tách bằng dấu phẩy).

SELECT GROUP_CONCAT(id), name, COUNT(*) c FROM documents GROUP BY name HAVING c > 1;

12
Tất cả thời gian này mà không biết về GROUP_CONCAT ()! rất rất hữu ích
aesede

Thực sự đánh giá cao Matt. Điều này thực sự hữu ích! Đối với những người đang cố cập nhật bằng phpmyadmin nếu bạn để id cùng với chức năng như thế này: SELECT id, GROUP_CONCAT(id), name, COUNT(*) c [...]nó cho phép chỉnh sửa nội tuyến và nó sẽ cập nhật tất cả các hàng liên quan (hoặc ít nhất là hàng đầu tiên khớp), nhưng không may là chỉnh sửa tạo ra lỗi Javascript. ..
Arm feet

Làm thế nào bạn sẽ tính toán có bao nhiêu id là đối tượng sao chép?
CMCDragonkai

2
Làm thế nào để tôi không được nhóm tất cả các ID, nhưng thay vào đó được liệt kê từ đầu đến cuối; với tất cả các giá trị tương ứng của chúng trong các cột bên cạnh chúng? Vì vậy, thay vì nhóm nó, nó chỉ hiển thị ID 1 và giá trị của nó, ID 2 và giá trị của nó. NGAY CẢ nếu các giá trị cho ID là như nhau.
MailBlade

1
Câu trả lời cực kỳ hữu ích, điều này nên được đặt lên hàng đầu để nhiều người nhìn thấy nó. Tôi nhớ mình đã trải qua bao nhiêu đau đớn khi tạo ra những danh sách như vậy và nó luôn có sẵn dưới dạng lệnh ..
John

13

Giả sử bảng của bạn được đặt tên là TableABC và cột mà bạn muốn là Col và khóa chính cho T1 là Khóa.

SELECT a.Key, b.Key, a.Col 
FROM TableABC a, TableABC b
WHERE a.Col = b.Col 
AND a.Key <> b.Key

Ưu điểm của phương pháp này so với câu trả lời ở trên là nó cung cấp cho Key.


4
+1 Vì nó tiện dụng. Mặc dù, trớ trêu thay, bản thân kết quả lại chứa các bản sao (nó liệt kê a và b, sau đó b và a.)
Fabien Snauwaert

2
@FabienSnauwaert Bạn có thể loại bỏ một số bản sao bằng cách so sánh ít hơn (hoặc lớn hơn)
Michael

@TechTravel. Hãy nghĩ câu trả lời của bạn rất rõ ràng, cảm ơn vì điều đó nhưng trên bàn lớn phải mất một thời gian (khoảng 2 triệu trên hơn 20.000 bảng mục) và sau khi hiển thị 25 kết quả đầu tiên, nếu tôi nhấp để hiển thị tiếp theo, lỗi hiển thị phpmyadmin "# 1052 - Cột 'id' trong mệnh đề thứ tự không rõ ràng "
bcag2

12
SELECT * 
FROM `dps` 
WHERE pid IN (SELECT pid FROM `dps` GROUP BY pid HAVING COUNT(pid)>1)

1
Không, bởi vì điều này hoàn toàn có thể là chậm nhất trong số rất nhiều. Các lựa chọn phụ nổi tiếng là chậm, vì chúng được thực thi cho mỗi hàng được trả về.
Oddman

10

Để tìm xem có bao nhiêu bản ghi trùng lặp trong cột tên trong Nhân viên, truy vấn bên dưới là hữu ích;

Select name from employee group by name having count(*)>1;

10

để có được tất cả các dữ liệu có chứa bản sao tôi đã sử dụng:

SELECT * FROM TableName INNER JOIN(
  SELECT DupliactedData FROM TableName GROUP BY DupliactedData HAVING COUNT(DupliactedData) > 1 order by DupliactedData)
  temp ON TableName.DupliactedData = temp.DupliactedData;

TableName = bảng bạn đang làm việc.

DupliactedData = dữ liệu trùng lặp mà bạn đang tìm kiếm.


Cái này hiển thị mỗi bản sao trong hàng riêng của nó. Đó là những gì tôi cần. Cảm ơn.
warmwhisky

8

Truy vấn cuối cùng của tôi kết hợp một vài câu trả lời ở đây có ích - kết hợp nhóm theo, đếm & GROUP_CONCAT.

SELECT GROUP_CONCAT(id), `magento_simple`, COUNT(*) c 
FROM product_variant 
GROUP BY `magento_simple` HAVING c > 1;

Điều này cung cấp id của cả hai ví dụ (được phân tách bằng dấu phẩy), mã vạch tôi cần và có bao nhiêu bản sao.

Thay đổi bảng và cột cho phù hợp.


8

Tôi không thấy bất kỳ phương pháp THAM GIA nào, có nhiều cách sử dụng về mặt trùng lặp.

Cách tiếp cận này cung cấp cho bạn kết quả nhân đôi thực tế.

SELECT t1.* FROM my_table as t1 
LEFT JOIN my_table as t2 
ON t1.name=t2.name and t1.id!=t2.id 
WHERE t2.id IS NOT NULL 
ORDER BY t1.name

2
FYI - Bạn sẽ muốn 'chọn somecol riêng biệt ..' nếu có tiềm năng tồn tại hơn 1 bản ghi trùng lặp nếu không kết quả sẽ chứa các bản sao của các hàng trùng lặp được tìm thấy.
vẽ

7
SELECT t.*,(select count(*) from city as tt
  where tt.name=t.name) as count
  FROM `city` as t
  where (
     select count(*) from city as tt
     where tt.name=t.name
  ) > 1 order by count desc

Thay thế thành phố bằng Bảng của bạn. Thay thế tên bằng tên trường của bạn



6

Tôi thấy kết quả trên và truy vấn sẽ hoạt động tốt nếu bạn cần kiểm tra giá trị cột đơn trùng lặp. Ví dụ email.

Nhưng nếu bạn cần kiểm tra với nhiều cột hơn và muốn kiểm tra kết hợp kết quả để truy vấn này sẽ hoạt động tốt:

SELECT COUNT(CONCAT(name,email)) AS tot,
       name,
       email
FROM users
GROUP BY CONCAT(name,email)
HAVING tot>1 (This query will SHOW the USER list which ARE greater THAN 1
              AND also COUNT)

Chính xác những gì cần thiết! Đây là truy vấn của tôi, kiểm tra 3 trường để tìm bản sao:SELECT COUNT(CONCAT(userid,event,datetime)) AS total, userid, event, datetime FROM mytable GROUP BY CONCAT(userid, event, datetime ) HAVING total>1
Kai Noack

4

Tôi thích sử dụng các chức năng có cửa sổ (MySQL 8.0+) để tìm các bản sao vì tôi có thể thấy toàn bộ hàng:

WITH cte AS (
  SELECT *
    ,COUNT(*) OVER(PARTITION BY col_name) AS num_of_duplicates_group
    ,ROW_NUMBER() OVER(PARTITION BY col_name ORDER BY col_name2) AS pos_in_group
  FROM table
)
SELECT *
FROM cte
WHERE num_of_duplicates_group > 1;

Trình diễn DB Fiddle


3
SELECT 
    t.*,
    (SELECT COUNT(*) FROM city AS tt WHERE tt.name=t.name) AS count 
FROM `city` AS t 
WHERE 
    (SELECT count(*) FROM city AS tt WHERE tt.name=t.name) > 1 ORDER BY count DESC

1
Làm cùng một truy vấn con hai lần có vẻ không hiệu quả.
NobleUplift


3
CREATE TABLE tbl_master
    (`id` int, `email` varchar(15));

INSERT INTO tbl_master
    (`id`, `email`) VALUES
    (1, 'test1@gmail.com'),
    (2, 'test2@gmail.com'),
    (3, 'test1@gmail.com'),
    (4, 'test2@gmail.com'),
    (5, 'test5@gmail.com');

QUERY : SELECT id, email FROM tbl_master
WHERE email IN (SELECT email FROM tbl_master GROUP BY email HAVING COUNT(id) > 1)

2
SELECT DISTINCT a.email FROM `users` a LEFT JOIN `users` b ON a.email = b.email WHERE a.id != b.id;

1
Đáng lưu ý rằng điều này chậm đến mức không thể chịu đựng được hoặc thậm chí có thể không hoàn thành nếu cột được truy vấn không được lập chỉ mục. Nếu không, tôi đã có thể thay đổi a.emailđể a.*và nhận được tất cả các ID của các hàng với các bản sao.
NobleUplift

@NobleUplift Bạn đang nói về cái gì?
Michael

@Michael Chà, vì nó đã ba tuổi nên tôi không thể kiểm tra bất kỳ phiên bản MySQL nào tôi đang sử dụng, nhưng tôi đã thử truy vấn này trên cơ sở dữ liệu mà cột tôi chọn không có chỉ mục trên đó, vì vậy nó mất khá nhiều Vài giây để kết thúc. Thay đổi nó để SELECT DISTINCT a.*giải quyết gần như ngay lập tức.
NobleUplift

@NobleUplift À ok. Tôi có thể hiểu nó đang chậm ... phần mà tôi quan tâm là "thậm chí có thể không hoàn thành".
Michael

@Michael Tôi không nhớ bảng nào trong hệ thống của chúng tôi, tôi phải chạy truy vấn này, nhưng đối với những bảng có vài triệu bản ghi có thể chúng sẽ hoàn thành, nhưng trong một thời gian dài mà tôi đã từ bỏ khi nhìn thấy nó thực sự sẽ kết thúc
NobleUplift

1

Để xóa các hàng trùng lặp với nhiều trường, trước tiên, hủy chúng thành khóa duy nhất mới được chỉ định cho các hàng riêng biệt, sau đó sử dụng lệnh "group by" để xóa các hàng trùng lặp với cùng một khóa duy nhất mới:

Create TEMPORARY table tmp select concat(f1,f2) as cfs,t1.* from mytable as t1;
Create index x_tmp_cfs on tmp(cfs);
Create table unduptable select f1,f2,... from tmp group by cfs;

bạn cũng có thể thêm một lời giải thích?
Robert

Tại sao không sử dụng CREATE TEMPORARY TABLE ...? Một lời giải thích nhỏ về giải pháp của bạn sẽ là tuyệt vời.
maxhb

1

Một đóng góp rất muộn ... trong trường hợp nó giúp bất kỳ ai chờ đợi ... Tôi có nhiệm vụ tìm các cặp giao dịch phù hợp (thực sự là cả hai mặt của chuyển khoản từ tài khoản) trong một ứng dụng ngân hàng, để xác định giao dịch nào là 'từ' và 'đến' cho mỗi giao dịch chuyển tiền giữa các tài khoản, vì vậy chúng tôi đã kết thúc với điều này:

SELECT 
    LEAST(primaryid, secondaryid) AS transactionid1,
    GREATEST(primaryid, secondaryid) AS transactionid2
FROM (
    SELECT table1.transactionid AS primaryid, 
        table2.transactionid AS secondaryid
    FROM financial_transactions table1
    INNER JOIN financial_transactions table2 
    ON table1.accountid = table2.accountid
    AND table1.transactionid <> table2.transactionid 
    AND table1.transactiondate = table2.transactiondate
    AND table1.sourceref = table2.destinationref
    AND table1.amount = (0 - table2.amount)
) AS DuplicateResultsTable
GROUP BY transactionid1
ORDER BY transactionid1;

Kết quả là các DuplicateResultsTablehàng cung cấp các hàng chứa các giao dịch khớp (nghĩa là trùng lặp), nhưng nó cũng cung cấp cùng một id giao dịch ngược lại lần thứ hai khớp với cùng một cặp, do đó, bên ngoài SELECTsẽ ở đó để nhóm theo ID giao dịch đầu tiên, được thực hiện bằng cách sử dụng LEASTGREATESTđể đảm bảo hai giao dịch luôn theo cùng một thứ tự trong kết quả, điều này làm cho nó an toàn với GROUPlần đầu tiên, do đó loại bỏ tất cả các kết quả trùng lặp. Chạy qua gần một triệu hồ sơ và xác định hơn 12.000 trận đấu chỉ trong chưa đầy 2 giây. Tất nhiên, giao dịch là chỉ số chính, thực sự có ích.


1
Select column_name, column_name1,column_name2, count(1) as temp from table_name group by column_name having temp > 1

1
SELECT ColumnA, COUNT( * )
FROM Table
GROUP BY ColumnA
HAVING COUNT( * ) > 1

3
Điều này là không chính xác vì nó cũng tìm thấy sự xuất hiện duy nhất. 0 nên là 1.
Kafoso

1

Nếu bạn muốn loại bỏ sử dụng trùng lặp DISTINCT

Nếu không, sử dụng truy vấn này:

SELECT users.*,COUNT(user_ID) as user FROM users GROUP BY user_name HAVING user > 1;


0

Hãy thử sử dụng truy vấn này:

SELECT name, COUNT(*) value_count FROM company_master GROUP BY name HAVING value_count > 1;
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.