Идентификация рядов, которые не соответствуют главному ряду

dba.stackexchange https://dba.stackexchange.com/questions/16711

Вопрос

Я сравниваю кучу таблиц из разных баз данных на разных серверах с главной записью. Мне нужно знать, какие серверы, идентифицированные 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

Это может быть не красиво, но должно работать

Лицензировано под: CC-BY-SA с атрибуция
Не связан с dba.stackexchange
scroll top