如何清除违反SQL Server 2008中主键的数据?

时间:2011-03-16 19:27:24

标签: sql-server tsql sql-server-2008

我从一个我无法控制的源代码中获得了一些糟糕的数据,它需要进入一个具有如下所示的复合主键的表:

PK_Part1, PK_Part2, StringData, DateData

我糟糕的数据有完全重复,PK重复有不同的StringData,PK重复有不同的DateData,PK重复有不同的StringData和DateData。

所以我可能会看到:

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但似乎在执行任何'MATCH'或'NO MATCH'语句之前比较Source表和Target表的所有行,所以我得到了PK违规,并删除了PK约束允许所有重复项。

4 个答案:

答案 0 :(得分:4)

如果您已经没有SQL Server中的数据:BULK INSERT进入临时表:

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语句,而不是基于CTE选择要保留BULK INSERT的数据。

答案 1 :(得分:2)

源数据应该进入临时表,保持临时区域。然后你可以从中选择最好的一个(因为你的样本数据包含重复的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 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);

答案 2 :(得分:1)

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

答案 3 :(得分:0)

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

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