Редактировать: Позвольте мне полностью перефразировать это, потому что я не уверен, что существует способ XML, который я описывал изначально.
Еще одно редактирование: это должен быть повторяемый процесс, и он должен быть настроен таким образом, чтобы его можно было вызывать в коде C#.
В базе данных A у меня есть набор таблиц, связанных между собой PK и FK. Скажем, родительская таблица с дочерними и внучатыми таблицами.
Я хочу скопировать набор строк из базы данных A в базу данных B, в котором есть таблицы и поля с одинаковыми именами. Для каждой таблицы я хочу вставить в одну и ту же таблицу в базе данных B. Но меня нельзя ограничить использованием одних и тех же первичных ключей. Процедура копирования должна создавать новые PK для каждой строки в базе данных B и должен распространять их на дочерние строки. Другими словами, я сохраняю те же отношения между данными, но не те же точные PK и FK.
Как бы вы это решили? Я открыт для предложений. SSIS не исключен полностью, но мне не кажется, что он будет делать именно это. Я также открыт для решения в LINQ, или с использованием типизированных наборов данных, или с использованием некоторого XML, или всего, что будет работать в SQL Server 2005 и / или C# (.NET 3.5). Лучшее решение не потребует SSIS и не потребует написания большого количества кода. Но я признаю, что этого «лучшего» решения может не быть.
(Я не придумывал ни эту задачу, ни ограничения; вот как это было дано мне.)
Нет, это должно быть повторяемо; в долгосрочной перспективе мы будем указывать, какие элементы копировать (например, по PK в исходной базе данных). Поскольку мы не можем гарантировать порядок, PK не должны быть точно такими же в новой базе данных. Репликация не исключена, если это хорошее решение.
Сохраняете ли вы (или можете ли вы сохранить) исходный идентификатор ПК в другом поле целевой базы данных?


Отбросьте подход XML и используйте мастер импорта / SSIS.
Мы бы предпочли по возможности держаться подальше от DTS / SSIS. Мы ищем что-то, что (должным образом упакованное в коде) легко запускается конечным пользователем, выбирая набор элементов для импорта из списка.
Во-первых, позвольте мне сказать, что SSIS - ваш лучший выбор. Но, отвечая на заданный вами вопрос ...
Я не верю, что вы сможете обойтись без создания новых идентификаторов повсюду, хотя вы могли бы, но вам нужно будет взять исходные идентификаторы для использования для поиска.
Лучшее, что вы можете получить, - это один оператор вставки для таблицы. Вот пример кода для выполнения SELECTs, чтобы получить данные из вашего образца XML:
declare @xml xml
set @xml='<People Key = "1" FirstName = "Bob" LastName = "Smith">
<PeopleAddresses PeopleKey = "1" AddressesKey = "1">
<Addresses Key = "1" Street = "123 Main" City = "St Louis" State = "MO" ZIP = "12345" />
</PeopleAddresses>
</People>
<People Key = "2" FirstName = "Harry" LastName = "Jones">
<PeopleAddresses PeopleKey = "2" AddressesKey = "2">
<Addresses Key = "2" Street = "555 E 5th St" City = "Chicago" State = "IL" ZIP = "23456" />
</PeopleAddresses>
</People>
<People Key = "3" FirstName = "Sally" LastName = "Smith">
<PeopleAddresses PeopleKey = "3" AddressesKey = "1">
<Addresses Key = "1" Street = "123 Main" City = "St Louis" State = "MO" ZIP = "12345" />
</PeopleAddresses>
</People>
<People Key = "4" FirstName = "Sara" LastName = "Jones">
<PeopleAddresses PeopleKey = "4" AddressesKey = "2">
<Addresses Key = "2" Street = "555 E 5th St" City = "Chicago" State = "IL" ZIP = "23456" />
</PeopleAddresses>
</People>
'
select t.b.value('./@Key', 'int') PeopleKey,
t.b.value('./@FirstName', 'nvarchar(50)') FirstName,
t.b.value('./@LastName', 'nvarchar(50)') LastName
from @xml.nodes('//People') t(b)
select t.b.value('../../@Key', 'int') PeopleKey,
t.b.value('./@Street', 'nvarchar(50)') Street,
t.b.value('./@City', 'nvarchar(50)') City,
t.b.value('./@State', 'char(2)') [State],
t.b.value('./@Zip', 'char(5)') Zip
from
@xml.nodes('//Addresses') t(b)
Это берет узлы из XML и анализирует данные. Чтобы получить реляционный идентификатор от людей, мы используем ../../ для продвижения вверх по цепочке.
Безусловно, самый простой способ - это средство сравнения данных SQL от Red Gate. Вы можете настроить его так, чтобы он делал то, что вы описали, через минуту или две.
Мне тоже нравятся SQL Compare и Data Compare от Red Gate, но, насколько я могу судить, они не соответствуют его требованиям к изменению первичных ключей.
Если перекрестные запросы к базе данных / связанные серверы являются вариантом, вы можете сделать это с помощью хранимой процедуры, которая копирует записи из родительского / дочернего в БД A во временные таблицы в БД B, а затем добавляет столбец для нового первичного ключа во временную дочернюю таблицу которые вы обновите после вставки заголовков.
У меня вопрос: если у записей нет одного и того же первичного ключа, как узнать, новая ли это запись? Есть ли другой ключ-кандидат? Если это новые таблицы, почему они не могут иметь одинаковый первичный ключ?
Я создал то же самое с набором хранимых процедур.
База данных B будет иметь свои собственные первичные ключи, но хранить первичные ключи базы данных A для целей отладки. Это означает, что у меня может быть более одной базы данных A!
Данные копируются через связанный сервер. Не слишком быстро; SSIS быстрее. Но SSIS не для новичков, а кодировать что-то, что работает с изменением исходных таблиц, непросто.
А из C# легко вызвать хранимую процедуру.
Я бы написал его в хранимой процедуре, используя вставки для тяжелой работы. Ваш код возьмет PK из таблицы A (предположительно через @@ Scope_Identity) - я предполагаю, что PK для таблицы A является полем Identity?
Вы можете использовать временные таблицы, курсоры или можете предпочесть среду CLR - она может подойти для такого рода операций.
Я был бы удивлен, если бы нашел инструмент, который мог бы сделать это в готовом виде с а) заранее определенными ключами или б) полями идентификации (очевидно, что в таблицах B и C их нет).
Вы очищаете целевые таблицы каждый раз, а затем начинаете заново? Это будет иметь большое значение для решения, которое вам нужно реализовать. Если вы каждый раз выполняете полный повторный импорт, вы можете сделать что-то вроде следующего:
Создайте временную таблицу или табличную переменную для записи старого и нового первичных ключей для родительской таблицы.
Вставьте данные родительской таблицы в место назначения и используйте предложение ВЫХОД, чтобы захватить новые идентификаторы и вставить их со старыми идентификаторами во временную таблицу. ПРИМЕЧАНИЕ. Использование предложения output является эффективным и позволяет выполнять массовую вставку без циклического перебора каждой вставляемой записи.
Вставьте данные дочерней таблицы. Присоединитесь к временной таблице, чтобы получить новый требуемый внешний ключ.
Вышеупомянутый процесс может быть выполнен с использованием сценария T-SQL, кода C# или SSIS. Я бы предпочел SSIS.
Нет, я добавляю к существующим данным в таблицах.
Если вы добавляете каждый раз, вам может потребоваться постоянная таблица для отслеживания взаимосвязи между первичными ключами исходной базы данных и первичными ключами целевой базы данных (по крайней мере, для родительской таблицы). Если вам нужно сохранить такие данные вне целевой базы данных, вы можете заставить SSIS хранить / извлекать их из какой-то базы данных журналов или даже из плоского файла.
Вероятно, вы можете избежать описанного выше сценария, если в родительской таблице есть комбинация полей, которые можно использовать для однозначной идентификации этой записи и, следовательно, «найти» первичный ключ для этой записи в целевой базе данных.
Я думаю, что, скорее всего, я буду использовать типизированные наборы данных. Это не будет обобщенное решение; нам придется регенерировать их, если какая-либо из таблиц изменится. Но судя по тому, что мне сказали, это не проблема; ожидается, что таблицы не сильно изменятся.
Наборы данных позволят достаточно легко просматривать данные в иерархическом порядке и обновлять PK из базы данных после вставки.
Имея дело с аналогичными задачами, я просто создал набор хранимых процедур для выполнения этой работы.
Поскольку указанная вами задача довольно нестандартная, вы вряд ли найдете «готовое к использованию» решение.
Просто чтобы дать вам несколько советов:
В хранимой процедуре:
Нет необходимости в курсорах и т. д., Просто сохраните немедленные результаты во временной таблице (или в табличной переменной, если работаете в одной хранимой процедуре).
Этот подход сработал для меня очень хорошо.
Конечно, вы можете добавить параметр в основную хранимую процедуру, чтобы вы могли копировать все новые записи или только те, которые вы указали.
Дайте мне знать, если это поможет.
Я думаю, что утилита SQL Server tablediff.exe может быть тем, что вы ищете.
См. Также эта ветка.
Я предполагаю, что это разовая операция, и репликация невозможна?