Domanda

Sto cercando di trasformare la query seguente in qualcosa di più leggibile e modificabile. La prima metà è identica alla seconda, ad eccezione del database richiesto (i nomi delle tabelle sono uguali).

  SELECT
    Column 1 AS c1,
    ...
    Column N AS cN
  FROM
    database1.dbo.Table1

UNION

  SELECT
    'Some String' as c1,
    ...
    NULL as cN
  FROM
    database1.dbo.Table2

UNION

  SELECT
    Column 1 AS c1,
    ...
    Column N AS cN
  FROM
    database2.dbo.Table1

UNION

  SELECT
    'Some String' as c1,
    ...
    NULL as cN
  FROM
    database2.dbo.Table2

Questa query è la definizione di DRY e mi sta chiamando per essere ri scritto, ma non ho idea di come!

MODIFICA : non possiamo usare linq e desideriamo risultati distinti; Sto cercando di ridurre la query in termini di dimensioni fisiche del file, non nei risultati restituiti.

MODIFICA : il database di cui sto interrogando è un database ERP proprietario. Ristrutturarlo non è un'opzione.

È stato utile?

Soluzione

Questo è un modello SQL piuttosto standard. A volte è facile trasferire inavvertitamente principi di codice OOP / procedurali come DRY su SQL, ma non sono necessariamente concetti trasferibili.

Nota quanto è facile eseguire il grok dell'intero progetto logico della query, rispetto alla ricerca di sottomoduli. Se una delle sottoespressioni avesse una colonna in più, o le colonne invertite, sporgerebbero. Questa è fondamentalmente un'istruzione SQL piuttosto semplice da usare come unità di esecuzione, dove disaggregarla la confonderebbe.

E quando esegui il debug, è utile poter utilizzare l'opzione di evidenziazione del testo dell'editor per esercitare selettivamente parti dell'istruzione - una tecnica che non esiste nel codice procedurale. OTOH, può diventare complicato cercare di rintracciare tutti i pezzi se sono sparsi in viste ecc. Anche i CTE possono rendere questo inconveniente.

Altri suggerimenti

Ho intenzione di uscire su un arto qui, e dire, in base alle informazioni che ci hai fornito;

È buono come sarà

Un suggerimento sulle prestazioni che vedo fuori dal pipistrello sta usando UNION ALL anziché UNION a meno che tu non voglia intenzionalmente record distinti. Un semplice <=> eliminerà i duplicati che richiedono tempo. <=> non lo fa.

Potresti riscriverlo con SQL dinamico e un ciclo, ma penso che il risultato sarebbe peggiore. Se c'è abbastanza codice duplicato per giustificare l'approccio sql dinamico, suppongo che potrebbe essere giustificato.

In alternativa, hai preso in considerazione l'idea di spostare la logica dalla procedura memorizzata in qualcosa come LINQ? Per molti, questa non è un'opzione, quindi sto solo chiedendo.

Un'ultima nota: resisti all'impulso di riparare ciò che non è rotto solo per renderlo più pulito. Se la pulizia aiuterà la manutenzione, la verifica, ecc., Quindi procedere.

Qual è il problema? Troppo lungo? Troppo ripetitivo?

A volte ottieni un brutto SQL - non puoi fare molto al riguardo.

Non vedo alcun modo per ripulirlo a meno che tu non voglia usare viste separate e poi unirle insieme.

Voto per le opinioni, che impongono un sovraccarico zero abbastanza vicino (OK, forse un piccolo costo di compilazione ma dovrebbe essere tutto). Quindi i tuoi proc diventano qualcosa del modulo

SELECT * FROM database1.view1
UNION
SELECT * FROM database1.view2
UNION
SELECT * FROM database2.view1
UNION
SELECT * FROM database2.view2

Non sono sicuro che vorrei condensarlo ulteriormente, anche se mi aspetto che la maggior parte delle piattaforme lo tollererebbe.

Sul tema SQL dinamico - ecco un esempio - non sono sicuro che sia meglio. Il vantaggio è che devi solo scrivere l'elenco SELECT una volta.

DECLARE @Select1 varchar(1000)
DECLARE @Select2 varchar(1000)

DECLARE @SQL varchar(4000)


SET @Select1 = 'SELECT
    Column 1 AS c1,
    ...
    Column N AS cN'


SET @Select2 = 'SELECT
    ''Some String'' as c1,
    ...
    NULL as cN'


SET @SQL = @Select1 + ' FROM database1.dbo.Table1 '

SET @SQL = @SQL + ' UNION ' + @Select2 + ' FROM database1.dbo.Table2 '

SET @SQL = @SQL + ' UNION ' + @Select1 + ' FROM database2.dbo.Table1 '

SET @SQL = @SQL + ' UNION ' + @Select2 + ' FROM database2.dbo.Table2 '


EXEC @SQL

Se tutti i tuoi processi appaiono così, probabilmente hai un problema architettonico.

Tutte le tue chiamate a table2 hanno solo un campo utile? (e a causa di UNION, finisci per avere solo una riga?)

Sono assolutamente d'accordo sull'idea di utilizzare SQL dinamico parametrizzato e / o generazione di codice per questo lavoro, arrivando persino a generare dinamicamente l'elenco di colonne usando INFORMATION_SCHEMA. Questo non è esattamente ciò di cui hai bisogno, ma è un inizio (potresti generare una tabella di database e tabelle):

DECLARE @template AS varchar(MAX)
SET @template = 'SELECT {@column_list} FROM {@database_name}.dbo.{@table_name}'
DECLARE @column_list AS varchar(MAX)

SELECT @column_list = COALESCE(@column_list + ',', '') + COLUMN_NAME
FROM database1.dbo.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = @table_name
ORDER BY ORDINAL_POSITION

DECLARE @sql AS varchar(MAX)
SET @sql = @template
SET @sql = REPLACE(@sql, '{@column_list}', @column_list)
SET @sql = REPLACE(@sql, '{@database_name}', @database_name)
SET @sql = REPLACE(@sql, '{@table_name}', @table_name)

A seconda del numero di righe restituite, è preferibile utilizzare UNION ALL sulle selezioni con attorno una query distinta. Ho già visto un problema simile in precedenza e avevo piani di esecuzione diversi per i due diversi stili

SELECT DISTINCT subquery.c1, subquery.cN
FROM
(
SELECT Column 1 AS c1, Column N AS cN FROM database1.dbo.Table1
UNION ALL
SELECT 'Some String' as c1, NULL as cN FROM database1.dbo.Table2
UNION ALL
SELECT Column 1 AS c1, Column N AS cN FROM database2.dbo.Table1
UNION ALL
SELECT 'Some String' as c1, NULL as cN FROM database2.dbo.Table2
) subquery
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top