Как очистить данные, которые нарушают первичный ключ в SQL Server 2008? - PullRequest
0 голосов
/ 16 марта 2011

У меня есть некоторые дрянные данные из источника, который я не могу контролировать, он должен войти в таблицу с составным первичным ключом, который выглядит следующим образом:

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», поэтому я получил нарушения PK, а снятие ограничения PK позволило включить все дубликаты.

Ответы [ 4 ]

4 голосов
/ 16 марта 2011

Если у вас еще нет этих данных в 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

Это «разделит» ваши данные на основе некоторых критериев (некоторый идентификатор или что-то еще), и каждый раздел данных будет упорядочен по дате (по убыванию - сначала самое новое).

Таким образом, запись с RowNum = 1 является самой новой записью для каждого раздела - выберите этот и выбросьте все остальные, и ваши данные будут очищены!

СОВЕТ: это предполагает, что ваша целевая таблица пуста! Если это не так, тогда да - вам может потребоваться вместо этого применить оператор MERGE, основанный на CTE, который выбирает данные для хранения из BULK INSERT.

2 голосов
/ 16 марта 2011

Данные из источника должны идти во временную таблицу, содержащую временную область. Затем вы можете выбрать лучший из них (так как ваши образцы данных содержат дубликаты 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);
1 голос
/ 16 марта 2011

Обычно мы помещаем такие данные в промежуточную таблицу, а затем избавляемся от дубликатов в промежуточной таблице, прежде чем пытаться выполнить оператор слияния.

0 голосов
/ 16 марта 2011

не уверен, что вы можете применить функцию длины строки в соединении, но если вы можете, попробуйте это:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...