Domanda

sto confrontando un gruppo di tabelle da database diversi su diversi server a un record master. Ho bisogno di sapere quali server, identificati da locationID, hanno le righe non corrispondenti perché potrebbero avere bisogno di manutenzione.

Ho una semplice query EXCEPT in cui mi confronto una tabella in cui ogni riga è la configurazione da ogni server; table1 ha una riga per server con tutte le configurazioni più locationID che è una colonna che mi dice quale server è. Io paragono questi tutti a un tavolo table1_master che ha le impostazioni corrette, ma escludo la locationID dal momento che non corrisponderà.

semplice query di seguito:

SELECT everything, but, locationID
FROM table1
EXCEPT
SELECT everything, but, locationID
FROM table1_master

Non solo è una di fila maestro mi confronto tutti i server a, e non seleziono di locationID qui.

Questo è un esempio delle righe che sto confronto. Ognuno ha una chiave primaria, un singolo varchar colonna e una lista gigante che è decine di colonne. Voglio mettere a confronto tutte le colonne tranne LocationID, ma ho bisogno LocationID per identificare le righe.

LocationID             setting    setting    setting     setting
CS02      C            Y           Y         Y           Y
CS03      C            Y           Y         Y           Y
CS06      C            Y           N         Y           Y

In questo esempio diciamo CS02 è il mio record Maestro, così dal momento che tutte le impostazioni sono gli stessi in CS02 e CS03, le righe non si presentano, ma CS06 di fa. Ma nella mia interrogazione EXCEPT, non sto in realtà cattura LocationID così Io in realtà non so quale riga è stata restituita.

Questo restituisce le righe di cui ho bisogno, ma non il locationID, quindi non so quali righe sono sbagliate. C'è un modo posso includere locationID nei risultati esposti, mentre a calci fuori le righe corrispondenti?

La soluzione che ho pensato è stato quello di fare una riga per ogni server nella tabella table1_master, in modo che ogni locationID è rappresentato, ma tutti hanno gli stessi dati diversi da quelli. La mia domanda EXCLUDE dovrebbe quindi restituire il locationID e le mie informazioni, ma è che il modo migliore per farlo?

È stato utile?

Soluzione

Si può anche fare questo con SQL dinamico senza dover costruire manualmente tutti i nomi delle colonne.

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;

Si può prendere l'output di questa query come è e memorizzare il posto di query, oppure è possibile commentare la SELECT e rimuovere il commento il EXEC e lasciare come SQL dinamico permanente - in questo caso sarà automaticamente adattarsi ai cambiamenti delle colonne della due tabelle.

Un'altra idea (supponendo LocationID è unico) - e mi venne in mente che si potrebbe voler includere la fila maestro in modo da poter individuare rapidamente le colonne che sono diversi:

  ;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;

Questa versione è un po 'meno (per lo più evitando la DISTINCT contro la tabella master, a costo di dover specificare tutte le colonne ancora una volta - che ancora una volta è possibile automatizzare di cui al precedente):

  ;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;

Tuttavia tutte queste opzioni sono esecutori più poveri con piani peggiori di semplice LEFT JOIN di Rachel. Ho cercato di attenersi al tema del usando EXCEPT, anche se è più sulla sintassi di prestazioni.

L'asporto chiave è che se il numero di colonne è troppo alto per affrontare manualmente, è possibile utilizzare l'approccio dinamico SQL sopra per costruire qualunque ricerca che si desidera utilizzare - e si può fare una volta e memorizzare il risultato, o avere il codice generato ogni volta. Per generare domanda di Rachel utilizzando SQL dinamico, non molto deve cambiare:

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;

Altri suggerimenti

mi raccomando:

  • Creazione di un campo Hash che è un persisteva colonna calcolata con una definizione sulla falsariga di HASHBYTES('SHA1', Field1 + Field2 + Field3...)
  • Confrontando solo che il valore HASH dal "master" per gli altri record
  • Visualizzazione tutti i valori reali righe non corrispondenti

Qualcosa di simile

SELECT *
FROM Table1
WHERE HashField <> (SELECT Hashfield FROM Table1_Master)

Cosa c'è di sbagliato con solo unendo le due tabelle su ogni colonna (o utilizzando una dichiarazione dove) e selezione delle voci che non esistono nel 2 ° tavola?

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

Potrebbe non essere abbastanza, ma dovrebbe funzionare

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a dba.stackexchange
scroll top