Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

7 cách tìm hàng trùng lặp trong SQL Server khi bỏ qua bất kỳ khóa chính nào

Dưới đây là bảy tùy chọn để tìm các hàng trùng lặp trong SQL Server, khi các hàng đó có khóa chính hoặc cột định danh duy nhất khác.

Nói cách khác, bảng chứa hai hoặc nhiều hàng chia sẻ chính xác các giá trị giống nhau trên tất cả các cột ngoại trừ cột định danh duy nhất của nó.

Dữ liệu Mẫu

Giả sử chúng ta có một bảng với dữ liệu sau:

SELECT * FROM Dogs;

Kết quả:

+---------+-------------+------------+
| DogId   | FirstName   | LastName   |
|---------+-------------+------------|
| 1       | Bark        | Smith      |
| 2       | Bark        | Smith      |
| 3       | Woof        | Jones      |
| 4       | Ruff        | Robinson   |
| 5       | Wag         | Johnson    |
| 6       | Wag         | Johnson    |
| 7       | Wag         | Johnson    |
+---------+-------------+------------+

Chúng ta có thể thấy rằng hai hàng đầu tiên là trùng lặp (ngoại trừ DogId , chứa một giá trị duy nhất trên tất cả các hàng và có thể được sử dụng làm cột khóa chính của bảng). Chúng ta cũng có thể thấy rằng ba hàng cuối cùng là trùng lặp (ngoại trừ DogId cột).

Cột ID duy nhất đảm bảo rằng không có hàng trùng lặp, đây thường là một đặc điểm rất được mong đợi trong RDBMS. Tuy nhiên, trong trường hợp này, nó có khả năng cản trở khả năng tìm thấy các bản sao của chúng tôi. Theo định nghĩa, cột ID duy nhất đảm bảo rằng không có bản sao. May mắn thay, chúng tôi có thể khắc phục vấn đề này khá dễ dàng, như các ví dụ sau đây cho thấy.

Tùy chọn 1

Có lẽ cách dễ nhất / đơn giản nhất để làm điều đó là với một truy vấn đơn giản sử dụng GROUP BY mệnh đề:

SELECT 
    FirstName, 
    LastName, 
    COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName;

Kết quả:

+-------------+------------+---------+
| FirstName   | LastName   | Count   |
|-------------+------------+---------|
| Wag         | Johnson    | 3       |
| Woof        | Jones      | 1       |
| Ruff        | Robinson   | 1       |
| Bark        | Smith      | 2       |
+-------------+------------+---------+

Chúng tôi có thể loại trừ khóa chính / cột ID duy nhất bằng cách bỏ qua nó khỏi truy vấn của chúng tôi.

Kết quả cho chúng ta biết rằng có ba hàng chứa Wag Johnson và hai hàng chứa Bark Smith. Đây là những bản sao (hoặc bản ba trong trường hợp của Wag Johnson).

Tùy chọn 2

Chúng tôi có thể loại trừ các trường hợp không trùng lặp khỏi kết quả bằng cách bao gồm HAVING mệnh đề trong truy vấn của chúng tôi:

SELECT 
    FirstName, 
    LastName, 
    COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName
HAVING COUNT(*) > 1;

Kết quả:

+-------------+------------+---------+
| FirstName   | LastName   | Count   |
|-------------+------------+---------|
| Wag         | Johnson    | 3       |
| Bark        | Smith      | 2       |
+-------------+------------+---------+

Tùy chọn 3

Chúng tôi cũng có thể kiểm tra các bản sao trên các cột được nối. Ví dụ:chúng ta có thể sử dụng CONCAT() hàm để nối hai cột của chúng ta:

SELECT
    DISTINCT CONCAT(FirstName, ' ', LastName) AS DogName,
    COUNT(*) AS Count
FROM Dogs
GROUP BY CONCAT(FirstName, ' ', LastName);

Kết quả:

+---------------+---------+
| DogName       | Count   |
|---------------+---------|
| Bark Smith    | 2       |
| Ruff Robinson | 1       |
| Wag Johnson   | 3       |
| Woof Jones    | 1       |
+---------------+---------+

Tùy chọn 4

Chúng tôi có thể sử dụng ROW_NUMBER() chức năng với PARTITION BY mệnh đề để tạo một cột mới với số hàng tăng lên mỗi khi có một bản sao, nhưng đặt lại lần nữa khi có một hàng duy nhất:

SELECT 
    *,
    ROW_NUMBER() OVER ( 
        PARTITION BY FirstName, LastName 
        ORDER BY FirstName, LastName
        ) AS Row_Number
FROM Dogs;

Kết quả:

+---------+-------------+------------+--------------+
| DogId   | FirstName   | LastName   | Row_Number   |
|---------+-------------+------------+--------------|
| 1       | Bark        | Smith      | 1            |
| 2       | Bark        | Smith      | 2            |
| 4       | Ruff        | Robinson   | 1            |
| 5       | Wag         | Johnson    | 1            |
| 6       | Wag         | Johnson    | 2            |
| 7       | Wag         | Johnson    | 3            |
| 3       | Woof        | Jones      | 1            |
+---------+-------------+------------+--------------+

Một lợi ích của phương pháp này là chúng tôi có thể thấy từng hàng trùng lặp, cùng với cột định danh duy nhất của nó, do thực tế là chúng tôi không nhóm các kết quả.

Tùy chọn 5

Chúng ta cũng có thể sử dụng ví dụ trước làm biểu thức bảng chung trong một truy vấn lớn hơn:

WITH cte AS 
    (
        SELECT 
            *,
            ROW_NUMBER() OVER ( 
                PARTITION BY FirstName, LastName 
                ORDER BY FirstName, LastName
                ) AS Row_Number
        FROM Dogs
    )
SELECT * FROM cte WHERE Row_Number <> 1;

Kết quả:

+---------+-------------+------------+--------------+
| DogId   | FirstName   | LastName   | Row_Number   |
|---------+-------------+------------+--------------|
| 2       | Bark        | Smith      | 2            |
| 6       | Wag         | Johnson    | 2            |
| 7       | Wag         | Johnson    | 3            |
+---------+-------------+------------+--------------+

Tùy chọn này loại trừ các bản không trùng lặp khỏi đầu ra.

Nó cũng loại trừ chính xác một hàng của mỗi bản sao khỏi đầu ra. Điều này sẽ mở ra cánh cửa để chúng tôi quay SELECT * cuối cùng thành một DELETE để xóa bảng trong khi vẫn giữ một trong mỗi bản sao.

Tùy chọn 6

Dưới đây là một cách ngắn gọn hơn để có được kết quả giống như ví dụ trước:

SELECT * FROM Dogs 
WHERE DogId IN (
    SELECT DogId FROM Dogs 
    EXCEPT SELECT MIN(DogId) FROM Dogs 
    GROUP BY FirstName, LastName
    );

Kết quả:

+-------+-----------+----------+
| DogId | FirstName | LastName |
+-------+-----------+----------+
|     2 | Bark      | Smith    |
|     6 | Wag       | Johnson  |
|     7 | Wag       | Johnson  |
+-------+-----------+----------+

Ví dụ này không yêu cầu tạo số hàng riêng của chúng tôi.

Tùy chọn 7

Và cuối cùng, đây là một kỹ thuật phức tạp hơn một chút để trả về các hàng trùng lặp:

SELECT * 
FROM Dogs d1, Dogs d2 
WHERE d1.FirstName = d2.FirstName 
AND d1.LastName = d2.LastName
AND d1.DogId <> d2.DogId 
AND d1.DogId = (
    SELECT MAX(DogId) 
    FROM Dogs d3 
    WHERE d3.FirstName = d1.FirstName 
    AND d3.LastName = d1.LastName
);

Kết quả:

+---------+-------------+------------+---------+-------------+------------+
| DogId   | FirstName   | LastName   | DogId   | FirstName   | LastName   |
|---------+-------------+------------+---------+-------------+------------|
| 2       | Bark        | Smith      | 1       | Bark        | Smith      |
| 7       | Wag         | Johnson    | 5       | Wag         | Johnson    |
| 7       | Wag         | Johnson    | 6       | Wag         | Johnson    |
+---------+-------------+------------+---------+-------------+------------+

Ngay cả khi kết quả trông phức tạp hơn, nhưng này, nó vẫn hiển thị cho chúng ta các bản sao!


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Truy vấn SQL để tìm ngày cuối cùng của tháng

  2. Giải phẫu các bế tắc của SQL Server và cách tốt nhất để tránh chúng

  3. Thay đổi mật khẩu khi đăng nhập SA trong SQL Server (Ví dụ T-SQL)

  4. Đo lường hiệu suất truy vấn:Chi phí truy vấn kế hoạch thực hiện so với thời gian thực hiện

  5. Cách trả về đầu ra của thủ tục đã lưu trữ thành một biến trong máy chủ sql