sql >> データベース >  >> RDS >> Sqlserver

主キーを無視してSQLServerで重複する行を見つける7つの方法

    これらの行に主キーまたはその他の一意の識別子列がある場合に、SQLServerで重複する行を見つけるための7つのオプションを次に示します。

    つまり、テーブルには、一意の識別子列を除いて、すべての列でまったく同じ値を共有する2つ以上の行が含まれています。

    サンプルデータ

    次のデータを含むテーブルがあるとします。

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

    最初の2行が重複していることがわかります(DogIdを除く) 列。すべての行で一意の値が含まれ、テーブルの主キー列として使用できます)。また、最後の3行が重複していることもわかります(DogIdを除く) 列)。

    一意のID列により、重複する行がないことが保証されます。これは通常、RDBMSで非常に望ましい特性です。ただし、この場合、重複を見つける機能を妨げる可能性があります。定義上、一意のID列は、重複がないことを保証します。幸い、次の例に示すように、この問題は非常に簡単に解決できます。

    オプション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       |
    +-------------+------------+---------+

    クエリからプライマリキー/一意のID列を除外することで、除外することができました。

    結果は、Wag Johnsonを含む3つの行と、BarkSmithを含む2つの行があることを示しています。これらは重複しています(またはWag Johnsonの場合は3回です)。

    オプション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()を使用できます 2つの列を連結する関数:

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

    この方法の利点の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            |
    +---------+-------------+------------+--------------+

    このオプションは、非重複を出力から除外します。

    また、各重複の1行だけを出力から除外します。これにより、最後のSELECT *を回すための扉が開かれます。 DELETEに 各複製を1つずつ保持しながら、テーブルの複製を解除します。

    オプション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. SQLでの条件付きUPDATEステートメントの使用

    2. SQL参加の質問

    3. make_interval()がPostgreSQLでどのように機能するか

    4. ORDERBY句を使用してビューを作成します