SQL代码返回线性递增重复数据的问题排查与修复请求
问题描述
我的SQL代码在运行9天周期(今日往前推8天)的同步任务时,插入dbo.finaltable的数据出现重复:今日的每条数据对应1行,昨日对应2条相同值,往前2天对应3条重复数据,直到第8天对应9条重复数据。代码如下:
declare @CurDate datetime -- Redo the last 7 days of transactions select @CurDate = cast(cast(dateadd(dd,-8,getdate()) as char(11)) as datetime) TRUNCATE TABLE dbo.finaltable WHILE @Curdate <= getdate() BEGIN DECLARE @temp table ( [StatsType] [char](5) NOT NULL, [EquipmentNo] [int] NOT NULL, [EquipmentType] [char](1) NOT NULL, [Terminal] [char](1) NOT NULL, [Param] [char](200) NULL, [Date] [datetime] NULL, [Error] [bit] NULL, [DayKey] [char](8) NULL, [TimeKey] [char](4) NULL, [DateTimeKey] bigint ) INSERT @temp EXEC SQLPRDC.serv.dbo.prproc @CurDate INSERT dbo.finaltable ( [StatsType] ,[EquipmentNo] ,[EquipmentType] ,[Terminal] ,[Param] ,[Date] ,[Error] ,[DayKey] ,[TimeKey] ,[DateTimeKey] ) SELECT [StatsType] ,[EquipmentNo] ,[EquipmentType] ,[Terminal] ,[Param] ,[Date] ,[Error] ,[DayKey] ,[TimeKey] ,[DateTimeKey] FROM @temp WHERE EquipmentType = 'M' SELECT @CurDate = dateadd(dd,1,@CurDate) END
对比之下,同类存储过程prproca无需临时表过滤即可正常运行,代码如下:
declare @CurDate datetime -- Redo the last 7 days of transactions select @CurDate = cast(cast(dateadd(dd,-8,getdate()) as char(11)) as datetime) TRUNCATE TABLE dbo.finaltable WHILE @Curdate <= getdate() BEGIN INSERT dbo.finaltable EXEC SQLPRDC.Rtostats.dbo.prproca @CurDate SELECT @CurDate = dateadd(dd,1,@CurDate) END
原因分析
核心问题出在存储过程prproc的逻辑上:它不是仅返回传入参数@CurDate当天的数据,而是返回从某个起始日期到@CurDate的所有历史数据。
循环执行时:
- 第一次循环(@CurDate=8天前):返回8天前的数据,插入finaltable
- 第二次循环(@CurDate=7天前):返回8天前+7天前的数据,过滤后插入,导致8天前的数据被重复插入一次
- ...
- 第九次循环(@CurDate=今日):返回8天前到今日的所有数据,过滤后插入,8天前的数据累计被插入9次,以此类推
而prproca能正常运行,说明它的逻辑是仅返回传入日期当天的数据,不会包含历史累积数据。
修复方案
方案1:修改存储过程prproc(根治)
检查并调整prproc的内部逻辑,确保它只返回传入参数@CurDate当天的统计数据,这是最彻底的解决办法,从源头避免重复数据。
方案2:在现有代码基础上做去重处理(无需修改存储过程)
如果无法修改prproc,可以在插入finaltable时加入去重逻辑,以下是两种可行方式:
方式A:每次插入前先清理对应日期的数据
在循环内插入前,先删除finaltable中当前@CurDate且EquipmentType='M'的数据,再插入临时表的过滤结果:
declare @CurDate datetime select @CurDate = cast(cast(dateadd(dd,-8,getdate()) as char(11)) as datetime) TRUNCATE TABLE dbo.finaltable WHILE @Curdate <= getdate() BEGIN DECLARE @temp table ( [StatsType] [char](5) NOT NULL, [EquipmentNo] [int] NOT NULL, [EquipmentType] [char](1) NOT NULL, [Terminal] [char](1) NOT NULL, [Param] [char](200) NULL, [Date] [datetime] NULL, [Error] [bit] NULL, [DayKey] [char](8) NULL, [TimeKey] [char](4) NULL, [DateTimeKey] bigint ) INSERT @temp EXEC SQLPRDC.serv.dbo.prproc @CurDate -- 新增:删除当前日期已存在的目标数据 DELETE FROM dbo.finaltable WHERE EquipmentType = 'M' AND CONVERT(date, [Date]) = CONVERT(date, @CurDate) INSERT dbo.finaltable ( [StatsType] ,[EquipmentNo] ,[EquipmentType] ,[Terminal] ,[Param] ,[Date] ,[Error] ,[DayKey] ,[TimeKey] ,[DateTimeKey] ) SELECT [StatsType] ,[EquipmentNo] ,[EquipmentType] ,[Terminal] ,[Param] ,[Date] ,[Error] ,[DayKey] ,[TimeKey] ,[DateTimeKey] FROM @temp WHERE EquipmentType = 'M' -- 新增:只取当前日期的数据 AND CONVERT(date, [Date]) = CONVERT(date, @CurDate) SELECT @CurDate = dateadd(dd,1,@CurDate) END
方式B:插入时使用DISTINCT去重(如果无唯一键约束)
如果finaltable没有唯一键约束,可以在插入的SELECT语句中加入DISTINCT,确保每条数据只插入一次:
-- 仅修改插入部分的SELECT语句 INSERT dbo.finaltable ( [StatsType] ,[EquipmentNo] ,[EquipmentType] ,[Terminal] ,[Param] ,[Date] ,[Error] ,[DayKey] ,[TimeKey] ,[DateTimeKey] ) SELECT DISTINCT -- 新增DISTINCT [StatsType] ,[EquipmentNo] ,[EquipmentType] ,[Terminal] ,[Param] ,[Date] ,[Error] ,[DayKey] ,[TimeKey] ,[DateTimeKey] FROM @temp WHERE EquipmentType = 'M'
方式C:一次性获取所有数据后去重插入
可以把循环去掉,直接一次调用prproc传入今日日期,获取所有8天内的数据,过滤后去重插入:
declare @CurDate datetime select @CurDate = cast(getdate() as date) TRUNCATE TABLE dbo.finaltable DECLARE @temp table ( [StatsType] [char](5) NOT NULL, [EquipmentNo] [int] NOT NULL, [EquipmentType] [char](1) NOT NULL, [Terminal] [char](1) NOT NULL, [Param] [char](200) NULL, [Date] [datetime] NULL, [Error] [bit] NULL, [DayKey] [char](8) NULL, [TimeKey] [char](4) NULL, [DateTimeKey] bigint ) INSERT @temp EXEC SQLPRDC.serv.dbo.prproc @CurDate INSERT dbo.finaltable ( [StatsType] ,[EquipmentNo] ,[EquipmentType] ,[Terminal] ,[Param] ,[Date] ,[Error] ,[DayKey] ,[TimeKey] ,[DateTimeKey] ) SELECT DISTINCT [StatsType] ,[EquipmentNo] ,[EquipmentType] ,[Terminal] ,[Param] ,[Date] ,[Error] ,[DayKey] ,[TimeKey] ,[DateTimeKey] FROM @temp WHERE EquipmentType = 'M' AND [Date] >= dateadd(dd,-8,getdate())
内容的提问来源于stack exchange,提问作者Scrub
相关产品推荐
相关产品推荐

