Sql server 在SQL Server 2008中,如何清理违反主键的数据?

Sql server 在SQL Server 2008中,如何清理违反主键的数据?,sql-server,tsql,sql-server-2008,Sql Server,Tsql,Sql Server 2008,我有一些来自我无法控制的源的蹩脚数据,它需要进入一个具有复合主键的表,该主键如下所示: pku第1部分,pku第2部分,StringData,DateData 我的蹩脚数据有完全重复,不同StringData的PK重复,不同DateData的PK重复,以及不同StringData和DateData的PK重复 所以我可能会看到: 1234,1234,Blah,2011-1-1 1234,1234,Blah,2011-1-1 4321,4321,Blah,2011-1-1 4321,4321,Bla

我有一些来自我无法控制的源的蹩脚数据,它需要进入一个具有复合主键的表,该主键如下所示:

pku第1部分,pku第2部分,StringData,DateData

我的蹩脚数据有完全重复,不同StringData的PK重复,不同DateData的PK重复,以及不同StringData和DateData的PK重复

所以我可能会看到:

1234,1234,Blah,2011-1-1
1234,1234,Blah,2011-1-1
4321,4321,Blah,2011-1-1
4321,4321,Blah,2011-10-10
5678,5678,Blah,2011-1-1
5678,5678,Blah1,2011-1-1
8765,8765,Blah,2011-1-1
8765,8765,Blah,2011-10-10
8765,8765,Blah1,2011-10-10
我如何在SQL Server 2008中清理此问题?鉴于此:
A) 我只需要与最新日期关联的数据
B) 我试图在源代码中强制解决有关字符串数据的问题,但就目前而言,字符串越长越好,相同的长度也可以。
C) 我必须假设源代码没有帮助,现在加载所有内容


我本来希望使用
MERGE
,但在执行任何“匹配”或“不匹配”语句之前,它似乎比较了源表和目标表的所有行,因此我得到了PK冲突,并且删除PK约束让所有重复项进入

我们通常将此类数据放入临时表,然后在尝试运行合并语句之前清除临时表中的重复数据。

如果SQL Server中还没有该数据:
将其批量插入到临时表中:

CREATE TABLE #tempStaging
(PK_Part1 INT, PK_Part2 INT, StringData VARCHAR(500), DateData DATE)

BULK INSERT #tempStaging
FROM 'c:\yourfile.txt'
WITH (FIELDTERMINATOR =',',
     ROWTERMINATOR ='\n')
然后,您应该能够执行以下操作:

;WITH CleaupData AS
(
  SELECT 
      PK_Part1, PK_Part2, StringData, DateData,
      ROW_NUMBER() OVER(PARTIION BY PK_Part1, PK_Part2
                        ORDER BY DateData DESC, LEN(StringData) DESC) as 'RowNum'
  FROM
      #tempStaging
)
INSERT INTO dbo.YourTargetTable(PK_Part1, PK_Part2, StringData, DateData)
    SELECT PK_Part1, PK_Part2, StringData, DateData 
    FROM CleanupData
    WHERE RowNum = 1
这将根据某些条件(某些ID或其他)对数据进行“分区”,并且每个数据分区都是按日期排序的(降序-最新优先)

因此,RowNum=1的条目是每个分区的最新条目-选择其中一个并扔掉所有其他条目,您的数据就会被清理


提示:这假设您的目标表为空!如果不是这样,那么是的-您可能需要应用一个
MERGE
语句,根据从
BULK INSERT
中选择要保留的数据的CTE,源的数据应该进入一个临时表,一个临时区域。然后您可以从中选择最好的一个(因为您的示例数据甚至在输入数据中也包含重复的part1+part2)

样品表和温度表

create table pkdup(
    PK_Part1 int, PK_Part2 int, StringData varchar(100), DateData datetime,
    primary key (PK_Part1,PK_Part2))
insert pkdup select 1234,1234,'', GETDATE()+1000

create table #tmp(col1 nvarchar(max), col2 nvarchar(max), col3 nvarchar(max), col4 datetime)
insert #tmp values
(1234,1234,'Blah','2011-1-1'),
(1234,1234,'Blah','2011-1-1'),
(4321,4321,'Blah','2011-1-1'),
(4321,4321,'Blah','2011-10-10'),
(5678,5678,'Blah','2011-1-1'),
(5678,5678,'Blah1','2011-1-1'),
(8765,8765,'Blah','2011-1-1'),
(8765,8765,'Blah','2011-10-10'),
(8765,8765,'Blah1','2011-10-10');
merge语句

merge pkdup as target
using (
    select col1, col2, col3, col4
    from (select *, row_number() over (
        partition by col1, col2
        order by col4 desc, len(col3) desc) rownum
        from #tmp) t
    where rownum=1 -- only the best
    ) as source
on source.col1=target.PK_Part1 and source.col2=target.PK_Part2
WHEN MATCHED AND (source.col4 > target.datedata or (source.col4=target.datedata and len(source.col3) > target.stringdata))
    THEN UPDATE SET target.stringdata = source.col3, target.datedata = source.col4
WHEN NOT MATCHED THEN
    INSERT (PK_Part1, PK_Part2, StringData, DateData)
    VALUES (source.col1, source.col2, source.col3, source.col4);

不确定是否可以在联接中应用字符串长度函数,但如果可以,请尝试以下操作:

select PK_Part1, PK_Part2, max_date, max_len, first(StringData) as first_string
from        
   (select PK_Part1, PK_Part2, max_date, max(len(StringData)) as max_len
    from table inner join
           (select PK_Part1, PK_Part2, max(DateData) as max_date
           from table
           group by
           PK_Part1, PK_Part2) md
    on table.PK_Part1 = md.PK_Part1 and 
           table.PK_Part2 = md.PK_Part2 and 
           table.DateData = md.max_date
    group by
           PK_Part1, PK_Part2, max_date) ml
   inner join table on 
           table.PK_Part1 = ml.PK_Part1 and 
           table.PK_Part2 = ml.PK_Part2 and 
           table.DateData = ml.max_date and
           len(table.StringData) = ml.max_len
   group by
           PK_Part1, PK_Part2, max_date, max_len

假设您有根据您的规则比现有数据更好的传入数据,您希望删除现有数据并插入新数据,对吗?顺便说一句,您对MERGE语句的理解是错误的-它在逐行级别进行合并,所以这可能就是你想要的want@Richardaka cyberkiwi:更新很好-它需要以某种方式替换它,但我仍在学习SQL,所以我很乐意听取有关此过程任何方面的建议。谢谢@理查德:回复:合并-你确定?我将检查我的merge语句,但它看起来非常简单。如果您可以首先使大多数值与例如LTRIM(RTRIM(xx))相等,则将日期设置为真正的日期时间,并以独特的方式选择它们。他还有一些关于绳子长度的东西。我假设如果日期相同,并且字符串A比字符串B长,那么取字符串B。因此,我会在DateColumn排序后按LEN(StringData)DESC输入一个顺序。但是我可能读错了。@Richard aka cyberkiwi:是的,如果您在没有PK的情况下将数据批量插入一个暂存表,或者您有一个需要清理的表(目前还没有强制执行PK),那么这样做是可行的。多亏了这一点,我本可以很容易地将其放到一个临时表中并合并。我真的很感谢你花这么多时间在这上面谢谢在我解决真正的问题之前,我已经把它简化了,还得喝点咖啡,但这正是我所需要的。现在来阅读一下分区是如何工作的。