如何编写 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
语句中,否则您的列将默认为空(这也不应该是默认值...)
我们是否还需要您的Route
和Placement
表的示例数据?
提示:不要使用 ancient "comma in FROM
"-syntax 连接。始终使用显式连接:a-gentle-introduction-to-sql.readthedocs.io/en/latest/part3/…
如果x
和y
代表坐标,那么你应该使用decimal
,而不是float
。 float
类型是近似类型,因此您无法执行(有意义的)相等检查。所以尽可能避免使用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 可以在发生性能问题时更容易调查)。
不要使用float
或real
类型来表示精确的数量(“精确”我不是指整数:你可以有精确的分数 数量),而应首选 decimal
类型。
在查询中引用表时,您应该始终包含架构名称(默认为dbo.
),因为它可以解决与歧义相关的问题。
在引用任何 UDF、UDT 和其他更现代的 SQL Server 功能时,现在需要包含架构名称。
这是因为performing equality-checks on float
and real
values in T-SQL is a pain。这包括评估JOIN
标准。
您的多对多链接表 dbo.Placement
也允许重复,因为它没有定义 PK。
不要使用像rid
、hid
和sid
这样的简短、隐晦的列名。 软件应该是自我记录的。我将这些列分别命名为RouteId
、HoldId
和SlotId
。
不要误以为将列命名为 Id
。列名不应该需要其父表的名称才能被理解(这是因为查询可以/将/确实公开您的数据,通常使用其原始列名,在没有原始表名的上下文中,例如在 CTE 中,派生 -表查询、VIEW
s 等)。
这是主观的,但我认为表名应该是复数,而不是单数(毕竟,一个表包含多行 - 如果该表只包含单行,我只会给一个表一个单数名称)。
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 查询以查找表中的重复项的主要内容,如果未能解决你的问题,请参考以下文章