Получение различий между двумя почти одинаковыми таблицами - PullRequest
0 голосов
/ 05 октября 2009

У меня есть процесс, который объединяет более 40 идентично структурированных баз данных в одну консолидированную базу данных, с той лишь разницей, что консолидированная база данных добавляет поле project_id к каждой таблице.

Чтобы быть максимально эффективным, я стараюсь копировать / обновлять запись из исходных баз данных в консолидированную базу данных, только если она была добавлена ​​/ изменена. Я удаляю устаревшие записи из консолидированной базы данных, а затем копирую в любые несуществующие записи. Для удаления устаревших / измененных записей я использую запрос, подобный следующему:

DELETE FROM <table> 
 WHERE NOT EXISTS (SELECT <primary keys> 
                     FROM <source> b 
                    WHERE ((<b.fields = a.fields>) or 
                          (b.fields is null and a.fields is null))) 
  AND PROJECT_ID = <project_id>

По большей части это работает, но в одной из таблиц в исходной базе данных содержится более 700 000 записей, и выполнение этого запроса занимает более часа.

Как сделать этот запрос более эффективным?

Ответы [ 4 ]

2 голосов
/ 05 октября 2009

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

0 голосов
/ 05 октября 2009

Я обычно переписываю подобные запросы, чтобы избежать ... Not In - это ужасно для производительности, хотя Not Exists улучшает это.

Ознакомьтесь с этой статьей, http://www.sql -server-pro.com / sql-where-clause-optim.html

Мое предложение ...

Выделите ваш столбец pkey в рабочую / временную таблицу, добавьте столбец (флаг) int default 0, не ноль, и индексируйте столбец pkey. Отметьте флаг = 1, если запись существует в вашем подзапросе (намного быстрее!). Замените ваш sub select в вашем основном запросе существующим где (выберите pkey из temptable, где flag = 0)

То, что это работает, - это возможность создать список значений «не существует», которые можно использовать включительно из набора «все включено».

Вот наш общий набор. {1,2,3,4,5}

Вот существующий набор {1,3,4}

Мы создаем наш рабочий стол из этих двух наборов (технически левое внешнее соединение) (Запись: существует)

{1: 1, 2: 0, 3: 1, 4: 1, 5: 0}

Наш набор «несуществующих записей»

{2,5} (выберите *, где флаг = 0)

Наш продукт ... и намного быстрее (индексы!)

{1,2,3,4,5} в {2,5} = {2,5}

{1,2,3,4,5} нет в {1,3,4} = {2,5}

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

Kris

0 голосов
/ 05 октября 2009

Вы можете изменить порядок оператора WHERE; он имеет четыре сравнения, в первую очередь проваливается одно.

Если вы можете немного изменить базы данных / приложение, и вам нужно будет сделать это снова, битовое поле с надписью «обновлено» может быть не плохим дополнением.

0 голосов
/ 05 октября 2009

Вы можете попробовать LEFT JOIN с фильтром NULL:

DELETE      <table> 
FROM        <table> t
LEFT JOIN   <source> b 
        ON (t.Field1 = b.Field1 OR (t.Field1 IS NULL AND b.Field1 IS NULL))
        AND(t.Field2 = b.Field2 OR (t.Field2 IS NULL AND b.Field2 IS NULL))
        --//...
WHERE       t.PROJECT_ID = <project_id>
        AND b.PrimaryKey IS NULL --// any of the PK fields will do, but I really hope you do not use composite PKs

Но если вы сравниваете все столбцы не-PK, ваш запрос будет страдать.

В этом случае лучше добавить поле UpdAt TIMESTAMP (как предлагает DVK) в обе базы данных, которые можно обновить с помощью триггера AFTER UPDATE, тогда процедура синхронизации будет намного быстрее, если вы создадите индекс, включающий PK и Обновлено в столбце.

...