Как эффективно сравнить два набора данных для различий? - PullRequest
0 голосов
/ 10 апреля 2020

Я создал пару запросов, которые представляют несколько точек данных о структуре нашей базы данных, один для таблиц и другой для представлений:

Таблицы:

SELECT t.TABLE_SCHEMA
    , t.TABLE_NAME
    , c.COLUMN_NAME
    , c.ORDINAL_POSITION
    , c.COLUMN_DEFAULT
    , c.IS_NULLABLE
    , c.DATA_TYPE
    , c.CHARACTER_MAXIMUM_LENGTH
    , c.NUMERIC_PRECISION
    , c.NUMERIC_PRECISION_RADIX
    , c.NUMERIC_SCALE
    , ccu.CONSTRAINT_NAME
    , tc.CONSTRAINT_TYPE
    , rc.UNIQUE_CONSTRAINT_NAME
    , rc.MATCH_OPTION
    , rc.DELETE_RULE
    , rc.UPDATE_RULE
FROM INFORMATION_SCHEMA.TABLES t
INNER JOIN INFORMATION_SCHEMA.COLUMNS c ON t.TABLE_NAME = c.TABLE_NAME
LEFT JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE ccu ON (c.TABLE_NAME = ccu.TABLE_NAME AND c.COLUMN_NAME = ccu.COLUMN_NAME)
LEFT JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc ON ccu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc ON ccu.CONSTRAINT_NAME = tc.CONSTRAINT_NAME
WHERE t.TABLE_TYPE = 'BASE TABLE'

Просмотров:

SELECT t.TABLE_SCHEMA AS 'VIEW_SCHEMA'
    , t.TABLE_NAME AS 'VIEW_NAME'
    , c.COLUMN_NAME
    , c.ORDINAL_POSITION
    , c.COLUMN_DEFAULT
    , c.IS_NULLABLE
    , c.DATA_TYPE
    , c.CHARACTER_MAXIMUM_LENGTH
FROM INFORMATION_SCHEMA.TABLES t
INNER JOIN INFORMATION_SCHEMA.COLUMNS c ON t.TABLE_NAME = c.TABLE_NAME
WHERE t.TABLE_TYPE = 'VIEW'

Мое намерение заключается в том, чтобы я мог выполнять запросы к нашей производственной среде и сохранять выходные данные в виде «снимка». Позже, когда мы внесем изменения в нашу среду разработки, я смогу выполнить там запросы и получить еще один «снимок». С текущими снимками и изменениями я мог бы сравнить их друг с другом, чтобы проанализировать различия.

Примечание: Да, я знаю такие вещи, как RedGate, и у них есть такие инструменты, но сторонние инструменты не являются для меня, где я нахожусь ... и, во-вторых, конечная цель состоит в том, чтобы иметь что-то, что я могу превратить в отчет, который можно отправить по электронной почте или в формате PDF, или как угодно.

Я выбираю привязывать все для пары Таблица / Столбец как «первичный ключ», так как это, кажется, лучший способ узнать, существует ли новый / удаленный / измененный столбец, и результаты моего запроса сохранены в две таблицы: [Meta].[SchemaChanges_New] и [Meta].[SchemaChanges_Current]. Я не включаю схему таблицы, так как хочу знать, является ли это изменением, а не использовать ее в JOIN, где это затем заставит изменение идентифицировать себя как элемент New vs Removed.

Итак, запрос определить новые элементы довольно просто:

SELECT n.*
FROM [Meta].[SchemaChanges_New] n
LEFT JOIN [Meta].[SchemaChanges_Current] c 
  ON (n.[TABLE_NAME] = c.[TABLE_NAME] AND n.[COLUMN_NAME] = c.[COLUMN_NAME])
WHERE (c.[TABLE_NAME] IS NULL OR c.[COLUMN_NAME] IS NULL)
ORDER BY n.[TABLE_NAME], n.[ORDINAL_POSITION]

... и запрос идентифицировать удаленные элементы в равной степени так:

SELECT c.*
FROM [Meta].[SchemaChanges_Current] c
LEFT JOIN [Meta].[SchemaChanges_New] n 
  ON (c.[TABLE_NAME] = n.[TABLE_NAME] AND c.[COLUMN_NAME] = n.[COLUMN_NAME])
WHERE (n.[TABLE_NAME] IS NULL OR n.[COLUMN_NAME] IS NULL)
ORDER BY n.[TABLE_NAME], n.[ORDINAL_POSITION]

* Если есть более подходящие способы выполнения sh это я тоже уши.

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

Первый удар в методе "грубой силы" старой школы, кажется, работает довольно прилично:

SELECT c.*, n.*
FROM [Meta].[SchemaChanges_New] n
INNER JOIN [Meta].[SchemaChanges_Current] c 
   ON (n.[TABLE_NAME] = c.[TABLE_NAME] AND n.[COLUMN_NAME] = c.[COLUMN_NAME])
WHERE c.TABLE_SCHEMA != n.TABLE_SCHEMA
   OR c.ORDINAL_POSITION != n.ORDINAL_POSITION
   OR IsNull(c.COLUMN_DEFAULT, '') != IsNull(n.COLUMN_DEFAULT, '')
   OR IsNull(c.IS_NULLABLE, '') != IsNull(n.IS_NULLABLE, '')
   OR IsNull(c.DATA_TYPE, '') != IsNull(n.DATA_TYPE, '')
   OR IsNull(c.CHARACTER_MAXIMUM_LENGTH, '') != IsNull(n.CHARACTER_MAXIMUM_LENGTH, '')
   OR IsNull(c.NUMERIC_PRECISION, '') != IsNull(n.NUMERIC_PRECISION, '')
   OR IsNull(c.NUMERIC_PRECISION_RADIX, '') != IsNull(n.NUMERIC_PRECISION_RADIX, '')
   OR IsNull(c.NUMERIC_SCALE, '') != IsNull(n.NUMERIC_SCALE, '')
   OR IsNull(c.CONSTRAINT_NAME, '') != IsNull(n.CONSTRAINT_NAME, '')
   OR IsNull(c.CONSTRAINT_TYPE, '') != IsNull(n.CONSTRAINT_TYPE, '')
   OR IsNull(c.UNIQUE_CONSTRAINT_NAME, '') != IsNull(n.UNIQUE_CONSTRAINT_NAME, '')
   OR IsNull(c.MATCH_OPTION, '') != IsNull(n.MATCH_OPTION, '')
   OR IsNull(c.DELETE_RULE, '') != IsNull(n.DELETE_RULE, '')
   OR IsNull(c.UPDATE_RULE, '') != IsNull(n.UPDATE_RULE, '')

Однако я обнаружил, что, когда с некоторыми столбцами связано более одного индекса, ложные срабатывания может появиться из-за "Simpisti c" JOIN. Но если я добавлю больше соединений в JOIN, я рискну изменить измененные элементы из набора результатов.

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

Спасибо за помощь!

1 Ответ

0 голосов
/ 10 апреля 2020

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

  binchk = binary_checksum(concat(t.TABLE_SCHEMA
                                , t.TABLE_NAME
                                , c.COLUMN_NAME
                                , c.ORDINAL_POSITION
                                , c.COLUMN_DEFAULT
                                , c.IS_NULLABLE
                                , c.DATA_TYPE
                                , c.CHARACTER_MAXIMUM_LENGTH
                                , c.NUMERIC_PRECISION
                                , c.NUMERIC_PRECISION_RADIX
                                , c.NUMERIC_SCALE
                                , ccu.CONSTRAINT_NAME
                                , tc.CONSTRAINT_TYPE
                                , rc.UNIQUE_CONSTRAINT_NAME
                                , rc.MATCH_OPTION
                                , rc.DELETE_RULE
                                , rc.UPDATE_RULE))

[concat обрабатывает нулевые значения]

...