Копирование реляционных данных из базы данных в базу данных - PullRequest
5 голосов
/ 12 сентября 2008

Редактировать : Позвольте мне полностью перефразировать это, потому что я не уверен, что есть способ XML, как я первоначально описывал.

Еще одно редактирование : Это должен быть повторяемый процесс, и его необходимо настроить так, чтобы его можно было вызывать в коде C #.

В базе данных A у меня есть набор таблиц, связанных между собой PK и FK. Допустим, родительский стол с дочерними и внуковыми столами.

Я хочу скопировать набор строк из базы данных A в базу данных B , которая имеет таблицы и поля с одинаковыми именами. Для каждой таблицы я хочу вставить одну и ту же таблицу в базу данных B. Но я не могу быть вынужден использовать одни и те же первичные ключи. Процедура копирования должна создавать новые PK для каждой строки в базе данных B и распространять их на дочерние строки. Другими словами, я придерживаюсь тех же отношений между данными, но не такими же точными ПК и ФК.

Как бы вы решили это? Я открыт для предложений. Служба SSIS не исключена полностью, но мне не кажется, что она сделает именно это. Я также открыт для решения в LINQ, или для использования типизированных DataSets, или для использования какой-то вещи XML, или для всего, что будет работать в SQL Server 2005 и / или C # (.NET 3.5). Лучшее решение не требует SSIS и не требует написания большого количества кода. Но я признаю, что это «лучшее» решение может не существовать.

(Я не решал эту задачу сам, ни ограничения; это то, как она была дана мне.)

Ответы [ 11 ]

2 голосов
/ 30 сентября 2008

Я думаю, что утилита SQL Server tablediff.exe может быть тем, что вы ищете.

См. Также эту тему .

1 голос
/ 12 сентября 2008

Во-первых, позвольте мне сказать, что SSIS - ваш лучший выбор. Но, чтобы ответить на вопрос, который вы задали ...

Я не верю, что вам удастся избежать создания новых идентификаторов повсюду, хотя вы могли бы, но вам нужно будет взять исходные идентификаторы для поиска.

Лучшее, что вы можете получить - это один оператор вставки для таблицы. Вот пример кода, который нужно сделать SELECT s, чтобы получить данные из вашего образца 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 и проанализировать данные. Чтобы получить реляционный идентификатор от людей, мы используем ../../, чтобы идти вверх по цепочке.

0 голосов
/ 30 сентября 2008

При работе с подобными задачами я просто создал набор хранимых процедур для выполнения этой работы.

Поскольку заданная вами задача довольно нестандартная, вы вряд ли найдете готовое к использованию решение.

Просто чтобы дать вам несколько советов:

  • Если базы данных находятся на разных серверах, используйте связанные серверы, чтобы вы могли обращаться к исходным и целевым таблицам просто через TSQL

В хранимой процедуре:

  • Определите родительские элементы, которые нужно скопировать - вы сказали, что первичные ключи различны, поэтому вам нужно использовать уникальные ограничения (вы должны быть в состоянии определить их, если таблицы нормализованы)
  • Определите дочерние элементы, которые необходимо скопировать, на основе идентифицированных родителей, чтобы проверить, есть ли некоторые из них уже в базе данных назначения. Снова используйте подход уникальных ограничений
  • Идентифицировать элементы внука (та же логика, что и у parent-child)
  • Копирование данных, начиная с самого низкого уровня (внуки, дети, родители)

Нет необходимости в курсорах и т. Д., Просто сохраняйте немедленные результаты во временной таблице (или табличной переменной, если работаете в одной хранимой процедуре)

Этот подход хорошо сработал для меня.

Конечно, вы можете добавить параметр в основную хранимую процедуру, чтобы вы могли скопировать все новые записи или только те, которые вы указали.

Дайте мне знать, если это поможет.

0 голосов
/ 18 сентября 2008

Я думаю, что, скорее всего, я собираюсь использовать типизированные наборы данных. Это не будет обобщенным решением; мы должны будем восстановить их, если какая-либо из таблиц изменится. Но на основании того, что мне сказали, это не проблема; таблицы не должны сильно меняться.

Наборы данных значительно упростят иерархическую обработку данных и обновят PK из базы данных после вставки.

0 голосов
/ 18 сентября 2008

Если вы добавляете каждый раз, то вам может потребоваться сохранить постоянную таблицу для отслеживания взаимосвязи между первичными ключами исходной базы данных и первичными ключами целевой базы данных (по крайней мере для родительской таблицы). Если вам нужно было сохранить данные этого типа в целевой базе данных, вы можете заставить SSIS хранить / извлекать их из какой-либо базы данных журналов или даже из простого файла.

Вероятно, вы могли бы избежать вышеописанного сценария, если в родительской таблице есть комбинация полей, которые можно использовать для уникальной идентификации этой записи и, следовательно, для «поиска» первичного ключа для этой записи в целевой базе данных.

0 голосов
/ 17 сентября 2008

Вы очищаете таблицы назначения каждый раз и затем начинаете снова? Это будет иметь большое значение для решения, которое вам нужно реализовать. Если вы делаете полный повторный импорт каждый раз, тогда вы можете сделать что-то вроде следующего:

Создание временной таблицы или табличной переменной для записи старого и нового первичных ключей для родительской таблицы.

Вставьте данные родительской таблицы в место назначения и используйте предложение OUTPUT , чтобы получить новые идентификаторы и вставить их со старыми идентификаторами во временную таблицу. ПРИМЕЧАНИЕ. Использование оператора вывода эффективно и позволяет выполнять массовую вставку без циклического перебора каждой вставляемой записи.

Вставить данные дочерней таблицы. Присоединитесь к временной таблице, чтобы получить новый требуемый внешний ключ.

Вышеописанный процесс может быть выполнен с использованием T-SQL Script, кода C # или служб SSIS. Я бы предпочел SSIS.

0 голосов
/ 16 сентября 2008

Я бы написал сценарий в хранимой процедуре, используя вставки для выполнения тяжелой работы. Ваш код возьмет PK из таблицы A (предположительно через @@ Scope_Identity) - я предполагаю, что PK для таблицы A - это поле Identity?

Вы можете использовать временные таблицы, курсоры или предпочитаете использовать CLR - это может пригодиться для такого рода операций.

Я был бы удивлен, найдя инструмент, который мог бы сделать это с полки либо с а) предварительно определенными ключами, либо б) полями идентификации (очевидно, в таблицах B и C их нет).

0 голосов
/ 16 сентября 2008

Я создал то же самое с набором хранимых процедур.

База данных B будет иметь свои собственные первичные ключи, но хранить первичные ключи базы данных A для целей отладки. Это означает, что я могу иметь более одной базы данных A!

Данные копируются через связанный сервер. Не слишком быстро; SSIS быстрее. Но SSIS не для начинающих, и нелегко кодировать то, что работает с изменением исходных таблиц.

И хранимую процедуру легко вызвать из C #.

0 голосов
/ 13 сентября 2008

Мне тоже нравится Red Gate SQL Compare and Data Compare, но, насколько я могу судить, он не отвечает его требованиям к изменению первичных ключей.

Если кросс-запросы к базе данных / связанные серверы являются опцией, вы можете сделать это с помощью хранимой процедуры, которая копирует записи из родительского / дочернего в БД A во временные таблицы в БД B, а затем добавляет столбец для нового первичного ключа в временная дочерняя таблица, которую вы обновите после вставки заголовков.

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

0 голосов
/ 13 сентября 2008

Самым простым способом является сравнение данных SQL в Red Gate. Вы можете настроить его так, чтобы он делал то, что вы описали, через минуту или две.

...