Идентификация рядов, которые не соответствуют главному ряду
-
22-10-2019 - |
Вопрос
Я сравниваю кучу таблиц из разных баз данных на разных серверах с главной записью. Мне нужно знать, какие серверы, идентифицированные locationID
, иметь не совпадающие ряды, потому что им может потребоваться техническое обслуживание.
У меня просто EXCEPT
Запрос, где я сравниваю таблицу, где каждая строка является конфигурацией с каждого сервера; table1
имеет одну строку на сервер со всеми конфигурацией плюс locationID
который является столбцом, который говорит мне, какой он сервер. Я сравниваю все с table1_master
Таблица, которая имеет правильные настройки, но я исключаю locationID
так как это не будет соответствовать.
Простой запрос ниже:
SELECT everything, but, locationID
FROM table1
EXCEPT
SELECT everything, but, locationID
FROM table1_master
Есть только один Master Row Я сравниваю все серверы, и я не выбираю его locationID
здесь.
Это пример рядов, которые я сравниваю. У каждого есть первичный ключ, один столбец varchar
и гигантский список этого десятки колонн. Я хочу сравнить все столбцы кроме LocationId, но мне нужно местоположение, чтобы идентифицировать строки.
LocationID setting setting setting setting
CS02 C Y Y Y Y
CS03 C Y Y Y Y
CS06 C Y N Y Y
В этом примере говорится, что CS02 является моей главной записью, поэтому, поскольку все настройки одинаковы в CS02 и CS03, эти ряды не отображаются, но CS06 делает. Но в моем EXCEPT
Запрос, я на самом деле не поймаю LocationId, поэтому я на самом деле не знаю, какой ряд был возвращен.
Это возвращает нужные мне ряды, но не locationID
, так что я не знаю, какие ряды не правы. Есть ли какой -нибудь способ включить locationID
В результатах, установленных во время выпуска соответствующих рядов?
Решение, о котором я думал, заключалось в том, чтобы сделать строку для каждого сервера в table1_master
таблица, так что каждый locationID
представлен, но все они имеют одинаковые данные, кроме этого. Мой EXCLUDE
Запрос должен затем вернуть locationID
И моя информация, но это лучший способ сделать это?
Решение
Вы также можете сделать это с динамическим SQL без необходимости вручную создавать все имена столбцов.
DECLARE @sql NVARCHAR(MAX), @c1 NVARCHAR(MAX), @c2 NVARCHAR(MAX);
SELECT @c1 = N'', @c2 = N'';
SELECT
@c1 = @c1 + ',' + QUOTENAME(name),
@c2 = @c2 + ' AND m.' + QUOTENAME(name) + ' = s.' + QUOTENAME(name)
FROM sys.columns
WHERE name <> 'LocationID'
AND [object_id] = OBJECT_ID('dbo.table1');
SET @sql = ';WITH s AS (
SELECT ' + STUFF(@c1, 1, 1, '') + ' FROM dbo.table1
EXCEPT
SELECT ' + STUFF(@c1, 1, 1, '') + ' FROM dbo.table1_master
)
SELECT m.LocationID
FROM s INNER JOIN dbo.table1 AS m ON 1 = 1
' + @c2;
SELECT @sql;
--EXEC sp_executesql @sql;
Вы можете взять результаты этого запроса как есть и сохранить запрос где -нибудь, или вы можете прокомментировать SELECT
и неуместно EXEC
и оставьте его как постоянную динамическую SQL - в этом случае он автоматически адаптируется к изменениям столбцов в двух таблицах.
Другая идея (при условии, что местоположение уникально) - и мне пришло в голову, что вы можете включить главную ряд, чтобы вы могли быстро обнаружить различные столбцы:
;WITH c AS
(
SELECT t.LocationID, m.setting1, m.setting2, ...
FROM dbo.table1 AS t CROSS JOIN dbo.table1_master AS m
)
SELECT DISTINCT src = '> master', setting1, setting2, ...
FROM c
UNION ALL
(
SELECT RTRIM(LocationID), setting1, setting2, ...
FROM dbo.table1
EXCEPT
SELECT RTRIM(LocationID), setting1, setting2, ...
FROM c
)
ORDER BY src;
Эта версия немного дешевле (в основном избегая DISTINCT
против главной таблицы, за счет необходимости указать все столбцы еще раз - что снова вы можете автоматизировать в соответствии с вышеизложенным):
;WITH m AS
(
SELECT setting1, setting2, ...
FROM dbo.table1_master
),
c AS
(
SELECT src = RTRIM(t.LocationID), m.setting1, m.setting2, ...
FROM dbo.table1 AS t CROSS JOIN m
)
SELECT src = '> master', setting1, setting2, ...
FROM m
UNION ALL
(
SELECT RTRIM(LocationID), setting1, setting2, ...
FROM dbo.table1
EXCEPT
SELECT src, setting1, setting2, ...
FROM c
)
ORDER BY src;
Однако все эти варианты являются худшими исполнителями с худшими планами, чем простые Рэйчел LEFT JOIN
. Анкет Я попытался придерживаться темы использования EXCEPT
Хотя это больше о синтаксисе, чем производительности.
Ключевой вывод состоит в том, что если количество столбцов слишком высок, чтобы справиться с ручным, вы можете использовать динамический подход SQL выше, чтобы построить любой запрос, который вы хотите использовать - и вы можете сделать это один раз и сохранить результат или иметь код генерируется каждый раз. Чтобы сгенерировать запрос Рэйчел, используя динамический SQL, не нужно изменить:
DECLARE @sql NVARCHAR(MAX), @and NVARCHAR(MAX), @anycol NVARCHAR(128);
SELECT @sql = N'', @and = N'';
SELECT @and = @and + ' AND t.' + QUOTENAME(name) + ' = m.' + QUOTENAME(name)
FROM sys.columns
WHERE [object_id] = OBJECT_ID('dbo.table1_master');
SELECT TOP (1) @anycol = QUOTENAME(name)
FROM sys.columns
WHERE [object_id] = OBJECT_ID('dbo.table1_master')
ORDER BY name;
SET @sql = 'SELECT locationID
FROM dbo.table1 AS t
LEFT OUTER JOIN dbo.table1_master AS m ON 1 = 1'
+ @and + ' WHERE m.' + @anycol + ' IS NULL;';
SELECT @sql;
--EXEC sp_executesql @sql;
Другие советы
Я бы порекомендовал:
- Создание а
Hash
поле, которое является сохраняемым вычисленным столбцом с определением в соответствии с линиямиHASHBYTES('SHA1', Field1 + Field2 + Field3...)
- Сравнивая именно это
HASH
ценность от вашего «мастера» до других ваших записей - Отображение всех фактических значений из не совпадающих рядов
Что-то типа
SELECT *
FROM Table1
WHERE HashField <> (SELECT Hashfield FROM Table1_Master)
Что плохого в том, чтобы просто присоединиться к двум таблицам в каждом столбце (или с использованием оператора, где), и выбрать элементы, которых нет во 2 -й таблице?
SELECT locationID
FROM table1
LEFT OUTER JOIN table1_master
ON table1.a = table1_master.a
AND table1.b = table1_master.b
AND table1.c = table1_master.c
WHERE table1_master.a is null
Это может быть не красиво, но должно работать