Отслеживание изменений в "чужой" таблице

Изерлонер
Дата: 29.04.2015 11:32:04
Поможите люди добрые!

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

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

Т.е. необходимо провести какое-то сканирование данных в той таблице, и если данные в какой-либо записи отличаются - внести изменения по записи, не трогая остальные записи. (ну и конечно сбросить сведения об изменениях в отдельный лог что решается триггерами на моей таблице).

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

Но по порядку:
1. Для начала для удобства работы сбросил нужные данные чужой таблицы во временную #tblOutcomeContent (подробности здесь опускаю, ибо к задаче не относятся)

2. Далее идет проверка на наличие новых записей в чужой таблице и вставка этих записей в таблицу моей базы:

INSERT tblOutcomeContent (iOutcome, iNum, iNumSk, iElement, iPart, smCount)
Select     _OC.iOutcome
,                   NUM
,                   NUMSK
,                   KOD
,                   PART
,                   CNT 
From #tblOutcomeContent _OC 
LEFT JOIN tblOutcomeContent OC 
ON _OC.iOutcome = OC.iOutcome 
Where OC.iOutcome is NULL
Print 'В таблицу tblOutcomeContent вставлено ' + Cast(@@ROWCOUNT as nvarchar) + ' строк'

При вставке срабатывает триггер, и вставленные записи так же попадают в лог.
3. Теперь необходимо проверить не изменялись ли какие-либо записи в чужой таблице, и при обнаружении изменений - провести эти изменения у меня. (Здесь я затупил и код не правильный! Т.к. по факту ни чего не меняет, ведь системе не понятно где менять):

UPDATE tblOutcomeContent
Set iNum = _OC.NUM, iNumsk = _OC.NUMSK, iElement = _OC.KOD, iPart = _OC.PART, smCount = _OC.CNT
From #tblOutcomeContent _OC Left Join tblOutcomeContent OC 
On _OC.iOutcome = OC.iOutcome 
And _OC.NUM = OC.iNum
And _OC.NUMSK = OC.iNumsk
And _OC.KOD = OC.iElement
And _OC.PART = OC.iPart
And _OC.CNT = OC.smCount
Where OC.iOutcome is Null
Print 'В таблице tblOutcomeContent изменено ' + Cast(@@ROWCOUNT as nvarchar) + ' строк'

По п. 3 пошли проблемы одна за другой пока вообще перестал понимать как мне добиться желаемого
3.1. Для начала прикинув что ключ в чужой таблице составной и состоит из полей iOutcome, KOD, PART переделал запрос на обновление
UPDATE tblOutcomeContent
Set iNum = _OC.NUM, iNumsk = _OC.NUMSK, iElement = _OC.KOD, iPart = _OC.PART, smCount = _OC.CNT
From #tblOutcomeContent _OC Join tblOutcomeContent OC 
On _OC.iOutcome = OC.iOutcome And _OC.KOD = OC.iElement And _OC.PART = OC.iPart
Where _OC.NUM <> OC.iNum
Or _OC.NUMSK <> OC.iNumsk
OR _OC.CNT <> OC.smCount
Where OC.iOutcome is Null
Print 'В таблице tblOutcomeContent изменено ' + Cast(@@ROWCOUNT as nvarchar) + ' строк'

Вот тут задам вопрос № 1: Если бы я оказался прав по поводу ключа насколько корректно решать задачу так? Может быть ее можно было решить проще? А если бы у меня таблица была с парой десятков полей это мне в условие Where надо было все эти поля загонять и сравнивать? А если в чужой таблице в поле Null - мне усложнять код добавляя функции типа Coalesce и т.п. Т.е. у меня стойкое ощущение что этот подход вроде и решает задачу - но можно (и должно) проще. А как не знаю.

Но продолжу. По дальнейшему я натурально в ступоре. Дело в том что чужая таблица вообще не имеет ключа (или ключом является тупо номер строки). Т.е. строки вообще могут повторяться (и повторяются - я проверял).

Вообще в рамках решаемых задач в моей базе данных это не мешает. Т.к. рассматриваемая таблица является перечнем материальных ценностей, типа перечня в счете-фактуре.
Ну забили например в счет-фактуру изоленту, потом через пару строк добавили ее же. Получилось две строки с изолентой, что в общем то ни на что не влияет, так как в итоге все учитывается (в общую стоимость по счету входит и стоимость первой изоленты и второй).
Но как отследить изменения вот в данном конкретном случае, когда нет ключа? Это вопрос № 2.

Вопросы касаются не одной таблицы, а целого ряда, где есть случаи и по вопросу №1 и по №2.
Изерлонер
Дата: 29.04.2015 11:35:13
По вставке тоже вижу ошибку с ключами, но мне бы сейчас сам принцип понять, а с ошибками разберусь.
iap
Дата: 29.04.2015 11:40:24
Изерлонер,

MERGE с OUTPUT Вам поможет.
С выводом $action для понимания произведенных действий (вставка, удаление, изменение).
https://msdn.microsoft.com/ru-ru/library/bb510625(v=sql.120).aspx
Изерлонер
Дата: 29.04.2015 11:48:26
iap
MERGE с OUTPUT

Похоже то что надо. Спасибо!
Пошел грызть гранит науки.
Glory
Дата: 29.04.2015 11:49:42
Изерлонер
Вообще в рамках решаемых задач в моей базе данных это не мешает. Т.к. рассматриваемая таблица является перечнем материальных ценностей, типа перечня в счете-фактуре.
Ну забили например в счет-фактуру изоленту, потом через пару строк добавили ее же. Получилось две строки с изолентой, что в общем то ни на что не влияет, так как в итоге все учитывается (в общую стоимость по счету входит и стоимость первой изоленты и второй).

А как происходит правка одной из изолент без ключа ?
Изерлонер
Дата: 29.04.2015 11:52:02
Glory
А как происходит правка одной из изолент без ключа ?

Сие мне не ведомо. Т.к. происходит в базе разработанной в Fox Pro еще в 1994 году. И как там решаются такие задачи я не понимаю. По всей видимости у записи таки есть некоторый не явный идентификатор, типа номера строки. Но при просмотре файлов той базы я этого идентификатора не вижу.
Glory
Дата: 29.04.2015 11:54:43
Изерлонер
Сие мне не ведомо. Т.к. происходит в базе разработанной в Fox Pro еще в 1994 году. И как там решаются такие задачи я не понимаю.

Причем тут Fox Pro ? В MSSQL нет никакого номера записи, как в Fox Pro. И для того, что написать UPDATE нужно WHERE указать условия фильтрации. И если под эти условия попадет больеш одной записи, то и обновлено будет больше одной записи
Изерлонер
Дата: 29.04.2015 12:02:41
Glory,

Понял. Да у меня такой задачи пока не стояло. По всей видимости теперь встала.
До сих пор данные этой таблицы служили только отражением данных той и не требовали вмешательства. Разве только суммирование по полям производил для каждой "счет-фактуры". Здесь ошибок не возникало.
У меня есть еще одна подобная таблица, там для ухода от этой проблемы я выполнял суммирование всех этих "изолент" по нескольким полям которые затем назначал ключевыми, приводил к нужному мне виду. Здесь может быть придется сделать так же.
Изерлонер
Дата: 29.04.2015 12:07:29
Изерлонер
приводил к нужному мне виду
Т.е. приводил к виду когда какой-то материал в счете фактуре может встретится только один раз. Тогда можно назначать составное ключевое поле:
ссылка на ИД счет-фактуры, ссылка на материал, ссылка на партию (материалы приходят партиями, и в партии цена одинаковая, в разных партиях цена может отличатся).
Изерлонер
Дата: 06.05.2015 05:28:02
Использую MERGE с OUTPUT. Что делать при наличии двух таблиц связанных между собой, при использовании MERGE последовательно на обеих таблицах (для сверки с соответствующими таблицами в чужой базе)? В MERGE в зависимости от условий производится - обновление/вставка/удаление сначала в одной, затем в другой таблице.
Но так как таблицы имеют связь PRIMARY KEY - FOREIGN KEY удаление записи в первой таблице может вызвать ошибку при наличии связанных с ней записью во второй таблице.
Если же MERGE применить сначала ко второй таблице - то при вставке в нее новых записей (для которых на текущий момент еще не существует соответствующей записи в первой таблице, она появится на следующем этапе при применении MERGE уже на этой таблице) - так же возникнет ошибка.
У меня пока только вариант оставить последовательность как есть (сначала таблица с PRIMARY KEY затем с FOREIGN KEY) и поставить каскадное удаление. Но этот вариант не нравится.