Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

7 начина за намиране на дублиращи се редове в SQL Server, като игнорирате първичен ключ

Ето седем опции за намиране на дублиращи се редове в SQL Server, когато тези редове имат първичен ключ или друга колона с уникален идентификатор.

С други думи, таблицата съдържа два или повече реда, които споделят абсолютно еднакви стойности във всички колони, с изключение на колоната с уникален идентификатор.

Примерни данни

Да предположим, че имаме таблица със следните данни:

SELECT * FROM Dogs;

Резултат:

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

Можем да видим, че първите два реда са дублирани (с изключение на DogId колона, която съдържа уникална стойност във всички редове и може да се използва като колона с първичен ключ на таблицата). Можем също да видим, че последните три реда са дублирани (с изключение на DogId колона).

Уникалната колона с идентификатор гарантира, че няма дублиращи се редове, което обикновено е много желана черта в RDBMS. В този случай обаче има потенциал да попречи на способността ни да намираме дубликати. По дефиниция колоната с уникален идентификатор гарантира, че няма дубликати. За щастие можем да преодолеем този проблем доста лесно, както показват следните примери.

Опция 1

Вероятно най-лесният/простият начин да го направите е с проста заявка, която използва GROUP BY клауза:

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

Резултат:

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

Успяхме да изключим колоната с първичен ключ/уникален идентификатор, като го пропуснем от нашата заявка.

Резултатът ни казва, че има три реда, съдържащи Уаг Джонсън и два реда, съдържащи Барк Смит. Това са дубликати (или три екземпляра в случая на Уаг Джонсън).

Опция 2

Можем да изключим недубликатите от резултата, като включим HAVING клауза в нашата заявка:

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

Резултат:

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

Опция 3

Можем също да проверим за дубликати на свързани колони. Например, можем да използваме CONCAT() функция за свързване на нашите две колони:

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

Резултат:

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

Опция 4

Можем да използваме ROW_NUMBER() функция с PARTITION BY клауза за създаване на нова колона с номер на ред, който се увеличава всеки път, когато има дубликат, но се нулира отново, когато има уникален ред:

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

Резултат:

+---------+-------------+------------+--------------+
| 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            |
+---------+-------------+------------+--------------+

Едно от предимствата на този метод е, че можем да видим всеки един дублиран ред, заедно с неговата колона с уникален идентификатор, поради факта, че не групираме резултатите.

Опция 5

Можем също да използваме предишния пример като общ табличен израз в по-голяма заявка:

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;

Резултат:

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

Тази опция изключва недубликатите от изхода.

Той също така изключва точно един ред от всеки дубликат от изхода. Това ни отваря вратата да обърнем последния SELECT * в DELETE за да премахнете дублирането на таблицата, като запазите по един от всеки дубликат.

Опция 6

Ето по-сбит начин да получите същия изход като предишния пример:

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

Резултат:

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

Този пример не изисква генериране на собствен номер на отделен ред.

Опция 7

И накрая, ето малко по-заплетена техника за връщане на дублиращи се редове:

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
);

Резултат:

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

Дори резултатът изглежда по-заплетен, но все пак ни показва дубликатите!


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Sql заявка за дървовидна таблица

  2. Какъв размер използвате за varchar(MAX) във вашата декларация за параметри?

  3. Изтрийте данни чрез функция с таблично значение в SQL Server

  4. Разлика между подзаявка и корелирана подзаявка

  5. Намерете индекса на последното появяване на подниз с помощта на T-SQL