如何编写 SQL 查询以查找表中的重复项

Posted

技术标签:

【中文标题】如何编写 SQL 查询以查找表中的重复项【英文标题】:How to write SQL query to find duplicates in tables 【发布时间】:2022-01-18 13:42:11 【问题描述】:

我目前正在尝试编写一个 SQL 查询,用于查找任何行具有相同 x 和 y 值的任何冲突。

这是我目前正在使用的表格:

CREATE TABLE Slot (
    sid  INT,
    wall varchar(200),
    x    FLOAT,
    y    FLOAT,

    PRIMARY KEY (sid)
)

CREATE TABLE Route (
    rid     INT,
    name    varchar(200),
    circuit varchar(200),

    PRIMARY KEY (rid)
)

CREATE TABLE Placement (
    rid INT FOREIGN KEY REFERENCES Route(rid),
    hid INT FOREIGN KEY REFERENCES Hold(hid),
    sid INT FOREIGN KEY REFERENCES Slot(sid)
)

所以我试图找到任何在同一面墙上并且具有相同 x 和 y 值的插槽。除此之外,我希望它们都是相同的 Route 电路。

我不知道我是否应该尝试使用“展示位置”的第三个表,因为我对此很陌生,并且在尝试加入它们时感到困惑,因为它们没有任何共享列。

这是我目前拥有的

SELECT
    DISTINCT
    S.sid

FROM
    Slot as S,
    Route as R

WHERE
    R.circuit = 'Beginner'

GROUP BY
    S.x,
    S.y,
    S.wall

HAVING
    COUNT(*) > 1

但这会引发错误,因为我必须在 GROUP BY 或聚合函数中使用“S.sid”,但我不想按此分组。

以下是我用来尝试的 INSERT 函数,并作为我目前所拥有的示例。

INSERT INTO Slot (sid, wall, x, y) VALUES (2345, 'south', 4, 7)
INSERT INTO Slot (sid, wall, x, y) VALUES (4534, 'south', 4, 7)
INSERT INTO Slot (sid, wall, x, y) VALUES (2456, 'west', 1, 7)

所以这里它会返回 sid 的 2345 和 4534,因为它们都在南墙上并且具有相同的 x 和 y 值。

【问题讨论】:

您需要将NOT NULL 添加到您的CREATE TABLE 语句中,否则您的列将默认为空(这也不应该是默认值...) 我们是否还需要您的RoutePlacement 表的示例数据? 提示:不要使用 ancient "comma in FROM"-syntax 连接。始终使用显式连接:a-gentle-introduction-to-sql.readthedocs.io/en/latest/part3/… 如果xy 代表坐标,那么你应该使用decimal,而不是floatfloat 类型是近似类型,因此您无法执行(有意义的)相等检查。所以尽可能避免使用float(和real)。 “除此之外,我希望它们都是相同的 Route 电路。” Placement 表已经允许相同的Slot(因为该表上根本没有PK 或UNIQUE 约束),此语句是模棱两可的。 (Hold 表的目的是什么?它如何影响您对“重复”数据的定义?),如果多个Slot 行具有相同的wall,x,y 值但没有任何对应的@,也会发生什么情况987654344@ 行?或者多个Placement 行对应Slot 中的相同和非重复 行? 【参考方案1】:

需要注意的一些事项:

JOIN 的古式,你做SELECT ... FROM x, y WHERE x.a = y.b,不应该使用。我希望现代 RDBMS 会阻止使用它的查询(在任何兼容模式之外)。 始终使用显式 JOIN 子句!为了可读性和可维护性(while performance shouldn't be different,使用显式 JOIN 可以在发生性能问题时更容易调查)。 不要使用floatreal 类型来表示精确的数量(“精确”我不是指整数:你可以有精确的分数 数量),而应首选 decimal 类型。 在查询中引用表时,您应该始终包含架构名称(默认为dbo.),因为它可以解决与歧义相关的问题。 在引用任何 UDF、UDT 和其他更现代的 SQL Server 功能时,现在需要包含架构名称。 这是因为performing equality-checks on float and real values in T-SQL is a pain。这包括评估JOIN 标准。 您的多对多链接表 dbo.Placement 也允许重复,因为它没有定义 PK。 不要使用像ridhidsid 这样的简短、隐晦的列名。 软件应该是自我记录的。我将这些列分别命名为RouteIdHoldIdSlotId。 不要误以为将列命名为 Id。列名不应该需要其父表的名称才能被理解(这是因为查询可以/将/确实公开您的数据,通常使用其原始列名,在没有原始表名的上下文中,例如在 CTE 中,派生 -表查询、VIEWs 等)。 这是主观的,但我认为表名应该是复数,而不是单数(毕竟,一个表包含多行 - 如果该表只包含单行,我只会给一个表一个单数名称)。 The worst argument I've heard so far 提倡单数而不是复数是因为(显然)一些 ORM 和代码生成工具缺乏将复数名词转换为单数名词的能力。 是的。 20 多年来,情况并非如此。

首先,为了避免在JOIN 条件中使用float 类型引起的问题,我将您的dbo.Slot 表更改为使用decimal

CREATE TABLE dbo.Slot2 (
    sid  int           NOT NULL,
    wall varchar(200)  NOT NULL,
    x    decimal(19,6) NOT NULL, -- 6 decimal places should be enough.
    y    decimal(19,6) NOT NULL,

    CONSTRAINT PK_Slot PRIMARY KEY ( sid ),

--  CONSTRAINT UK_SlotValues UNIQUE ( wall, x, y ) -- This will prevent duplicate values in future.
);

INSERT INTO dbo.Slot2 ( sid, wall, x, y )
SELECT
    sid,
    wall,
    CONVERT( decimal(19,6), x ) AS x2,
    CONVERT( decimal(19,6), y ) AS y2
FROM
    dbo.Slot;
    
DROP TABLE dbo.Slot;

EXEC sp_rename 'dbo.Slot2', 'Slot';

考虑到这一点,现在让我们在槽集合中获取重复值(即找到相同的wall, x, y没有其他值):

SELECT
    wall,
    x,
    y
FROM
    dbo.Slot
GROUP BY
    wall,
    x,
    y
HAVING
    COUNT(*) >= 2

然后我们在原始dbo.Slot这组重复值之间做一个INNER JOIN,以及添加一个ROW_NUMBER值以便更容易选择单行如果删除了其他重复项,请保留:

WITH duplicateValues (
    SELECT
        wall,
        x,
        y
    FROM
        dbo.Slot
    GROUP BY
        wall,
        x,
        y
    HAVING
        COUNT(*) >= 2
)
SELECT
    ROW_NUMBER() OVER ( PARTITION BY s.wall, s.x, s.y ORDER BY s.sid ) AS n,
    s.*
FROM
    dbo.Slot AS s
    INNER JOIN duplicateValues AS d ON
        s.wall = d.wall
        AND
        s.x = d.x
        AND
        s.y = d.y


在您的帖子中,您提到还想考虑Placement 表,但是我们需要更多详细信息,因为您的帖子没有解释Placement 表应该如何工作。

但是,您的 Placement 表仍应有 PK。我假设Placement 表的HoldId 列不是关键列,所以应该看起来像这样:

CREATE TABLE dbo.Placement (
    RouteId int NOT NULL,
    SlotId  int NOT NULL,
    HoldId  int NOT NULL,

    CONSTRAINT PK_Placement PRIMARY KEY ( RouteId, SlotId ),
    
    CONSTRAINT FK_Placement_Route FOREIGN KEY ( RouteId ) REFERENCES dbo.Route ( rid ),
    CONSTRAINT FK_Placement_Slot  FOREIGN KEY ( SlotId  ) REFERENCES dbo.Slot ( sid ),
    CONSTRAINT FK_Placement_Hold  FOREIGN KEY ( HoldId  ) REFERENCES dbo.Hold ( hid )
);

【讨论】:

以上是关于如何编写 SQL 查询以查找表中的重复项的主要内容,如果未能解决你的问题,请参考以下文章

如何编写 BigQuery/SQL 查询以将一个表中的列的平均值与第二个/另一个表中的列相除

sql SQL查询使用单个值查找表中的重复项。

SQL 查询以查找由字母分隔的重复项

如何编写 SQL 查询以从表中提取 50% 的记录?

如何避免两次编写 SQL Server 查询以避免重复?

在 SQL 表中查找所有相似的行