返回在 MariaDB 中具有主键的重复行的 7 种方法

发布于

以下是当 MariaDB 中具有主键或其他唯一标识符列时返回重复行的七种方法。

因此,重复行在所有列中共享完全相同的值,但其唯一标识符列除外。

示例数据

我们将使用以下数据作为示例:

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

主键列确保没有重复的行,这在RDBMSs中通常是一件好事。但是,根据定义,这意味着没有重复项。在我们的例子中,主键列是一个递增的数字,它的值没有任何意义,也不重要。因此,如果我们想_在重要的列_中查找重复项,我们需要忽略该行。

选项 1

我们可以使用 theclause 按列的有效列对列进行分组,然后使用 thefunction 返回相同行数:GROUP BYCOUNT()

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

结果:

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

我们能够通过从查询中省略主键列来排除它。

结果告诉我们,有两行包含Bark Smith,三行包含Wag Johnson。这些是重复的(在Wag Johnson的情况下是一式三份)。其他两行没有任何重复项。

选项 2

我们可以使用子句从输出中排除非重复项:HAVING

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

结果:

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

选项 3

还可以检查串联列上的重复项。例如,我们可以使用 thefunction 连接我们的两列,使用 thekeyword 获取不同的值,然后使用 thefunction 返回计数:CONCAT()DISTINCT``COUNT()

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 | | 6 | Wag | Johnson | 1 | | 5 | 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 | | 5 | 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 | +——-+———–+———-+

此示例不需要生成我们自己的单独行号。

我们可以替换以删除重复项。SELECT *``DELETE

选项 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 | +——-+———–+———-+——-+———–+———-+