从表中删除重复的 ID - 性能改进
Posted
技术标签:
【中文标题】从表中删除重复的 ID - 性能改进【英文标题】:Delete repeated Ids from Table - Performance Improvement 【发布时间】:2016-08-26 14:38:47 【问题描述】:我有一张重复代码的表格,我需要清理表格以删除重复的代码,但表格中至少有一个左侧。
我的桌子是这样的:
FriendlyFunctionCode MemberFirmId FunctionLevel3Desc
1 Value1 Value2
1 Value2 Value3
2 Value4 Value5
我需要这样的东西:(剩下哪一行并不重要,至少要有一个)
FriendlyFunctionCode MemberFirmId FunctionLevel3Desc
1 Value1 Value2
2 Value4 Value5
我有这个查询,但性能很糟糕
SELECT MemberFirmId, FriendlyFunctionCode
INTO #ToDeleteRepeated
FROM [dbo].[FirmFunction]
GROUP BY MemberFirmId, FriendlyFunctionCode
HAVING COUNT(1) > 1
DECLARE @Code VARCHAR(100), @Desc VARCHAR(250)
WHILE ((SELECT COUNT(1) FROM #ToDeleteRepeated) > 0)
BEGIN
SELECT TOP 1 @Code = FriendlyFunctionCode FROM #ToDeleteRepeated
WHILE ((SELECT COUNT(1) FROM [FirmFunction] WHERE FriendlyFunctionCode = @Code) > 0)
BEGIN
SELECT TOP 1 @Desc = FunctionLevel3Desc FROM [FirmFunction] WHERE FriendlyFunctionCode = @Code
DELETE FROM [FirmFunction] WHERE FriendlyFunctionCode = @Code AND FunctionLevel3Desc = @Desc
END
END
有什么建议吗?
【问题讨论】:
首先,当它们具有与 FriendlyFunctionCode 相关的不同值时,您是否关心您保留或删除哪些记录?您的查询表明您没有。实际上只是要注意,如果 FunctionLevel3Desc 也重复,您的查询将删除多条记录。 【参考方案1】:WITH CTE AS (SELECT MemberFirmId, FriendlyFunctionCode,
ROW_NUMBER() over (PARTITION by FriendlyFunctionCode ORDER BY FriendlyFunctionCode ) AS RN
FROM [dbo].[FirmFunction]
)
DELETE CTE WHERE CTE.RN >1
【讨论】:
我认为你只需要FriendlyFunctionCode
分区
很棒的答案,也试过了,效果很好。【参考方案2】:
使用 CTE 和 row_number() 删除
;with cte as (
select *, row_number() over(partition by friendlyfunctioncode order by memberfirmid) rn
from deletingtable)
delete from cte where rn > 1
这将按照以下执行计划执行:
表/聚集索引扫描 --> 排序(如果没有索引) --> 段 --> 序列项目 --> 过滤然后删除,
如果它在 FriendlyFunctionCode 上有正确的索引,它在单次扫描中执行得更快
【讨论】:
表上可能的附加索引怎么样? 没有重复删除。 意思是,如果有更多的索引,那么这些索引会因为删除而产生开销【参考方案3】:您可以使用这样的窗口函数。无需使用游标(在 SQL Server 中表现不佳)。您可以自行运行内部选择以查看它对行号的作用。
测试数据
CREATE TABLE #TestData (FriendlyFunctionCode int, MemberFirmId nvarchar(10), FunctionLevel3Desc nvarchar(10))
INSERT INTO #TestData
VALUES
(1,'Value1','Value2')
,(1,'Value2','Value3')
,(2,'Value4','Value5')
查询
SELECT
a.FriendlyFunctionCode
,a.MemberFirmId
,a.FunctionLevel3Desc
INTO #SavedData
FROM
(
SELECT
FriendlyFunctionCode
,MemberFirmId
,FunctionLevel3Desc
,ROW_NUMBER() OVER(PARTITION BY FriendlyFunctionCode ORDER BY FriendlyFunctionCode) RowNum
FROM #TestData
) a
WHERE a.RowNum = 1
TRUNCATE TABLE #TestData
INSERT INTO #TestData (FriendlyFunctionCode, MemberFirmId, FunctionLevel3Desc)
SELECT
FriendlyFunctionCode
,MemberFirmId
,FunctionLevel3Desc
FROM #SavedData
DROP TABLE #SavedData
结果
FriendlyFunctionCode MemberFirmId FunctionLevel3Desc
1 Value1 Value2
2 Value4 Value5
【讨论】:
正确的概念,但实际上并没有删除记录 这很公平。我已经更新了我的答案以截断原始表并插入保存的记录。应该比删除更好。【参考方案4】:您可以在 FunctionCode 上使用 MAX 和分组。
SELECT
FriendlyFunctionCode,
MAX(MemberFirmId) as MemberFirmId,
MAX(FunctionLevel3Desc) as FuncationLevel3Desc
INTO #StagingTable
FROM
FirmFunction
GROUP BY
FriendlyFunctionCode
然后截断您的表,然后选择回其中...或者只是一起创建一个表并将不同的(最大)记录插入其中。
TRUNCATE TABLE FirmFunction
INSERT INTO FirmFunction (FriendlyFunctionCode,MemberFirmId,FunctionLevel3Desc)
SELECT * FROM #StagingTable
这比创建一个表 FirmFunction2 更安全
SELECT TOP 1 INTO FirmFunction2 FROM FirmFunction WHERE 1=0
INSERT INTO FirmFunction2 (FriendlyFunctionCode, MemberFirmId, FunctionLevel3Desc)
SELECT
FriendlyFunctionCode,
MAX(MemberFirmId) as MemberFirmId,
MAX(FunctionLevel3Desc) as FuncationLevel3Desc
INTO #StagingTable
FROM
FirmFunction
GROUP BY
FriendlyFunctionCode
然后您可以检查 FirmFunction2 中的日期,如果您满意...在删除另一个表后重命名它。
【讨论】:
好方法,修改它并像魅力一样工作。谢谢。 @Rednaxel 我只想强调 CTE 很棒,但使用时要格外小心。通常初学者(不是说你是初学者)像临时表一样使用它们,而临时表不能使用。这通常没有任何问题,除了 CTE 会影响原始数据源,因为您在您的情况下有意这样做。因此,如果您使用 CTE 执行和插入、删除等操作,则无法返回。确保语法正确...您正确分区和排序等等。我提供的方法没有那么精简,但不太容易出错。 感谢您的 cmets,我会小心 CTE。以上是关于从表中删除重复的 ID - 性能改进的主要内容,如果未能解决你的问题,请参考以下文章