Domanda

Mi chiedo come gli altri gestiscono questa situazione ... e come applicare il principio Don't Repeat Yourself (DRY) a questa situazione.

Mi ritrovo costantemente a PIVOTARE o scrivere istruzioni CASE in T-SQL per presentare i mesi come colonne. In genere ho alcuni campi che includeranno (1) un campo data e (2) un campo valore. Quando lo presento a un utente tramite una pagina ASPX o Reporting Services, devo avere le ultime 14 colonne all'estrema destra per avere questo schema:

[Anno], [Jan], [febbraio], [Mar], [aprile], [maggio], [giugno], [luglio], [agosto], [sep], [ottobre], [novembre] , [Dec], [totale]

Dove anno è l'anno come int e ogni altro campo è il campo valore sommato per quel mese (tranne per [Totale] che è il campo valore totale per l'anno).

Vorrei trovare un modo riutilizzabile per gestirlo. Aperto a tutti i suggerimenti (T-SQL / ANSI SQL)

È stato utile?

Soluzione

Questo non è esattamente quello che stai cercando, ma ho fatto un sacco di UNPIVOT ripetitivi e, in genere, vorrei codificare questo gen, con un qualche tipo di denominazione standardizzata e usa pesantemente i CTE:

WITH P AS (
    SELECT Some Data
            ,[234] -- These are stats
            ,[235]
    FROM Whatever
     )
,FINAL_UNPIVOTED AS (
    SELECT Some Data
            ,[STAT]
    FROM P
    UNPIVOT (
        STAT FOR BASE IN ([234], [235]) 
    ) AS unpvt
    WHERE STAT <> 0
)
SELECT Some Data
              ,CONVERT(int, FINAL_UNPIVOTED.[BASE]) AS [BASE]
              ,FINAL_UNPIVOTED.[STAT]
FROM FINAL_UNPIVOTED

Puoi codegen ispezionando una tabella o una vista e usando qualcosa del genere:

DECLARE @sql_unpivot AS varchar(MAX)
SELECT @sql_unpivot = COALESCE(@sql_unpivot + ',', '') + COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'whatever'

E modello del codice:

SET @template = '
    WITH P AS (
        SELECT Some Data
                ,{@sql_unpivot}
                  FROM Whatever
         )
    ,FINAL_UNPIVOTED AS (
        SELECT Some Data
                ,[STAT]
        FROM P
        UNPIVOT (
            STAT FOR BASE IN ({@sql_unpivot}) 
        ) AS unpvt
        WHERE STAT <> 0
    )
    SELECT Some Data
                  ,CONVERT(int, FINAL_UNPIVOTED.[BASE]) AS [BASE]
                  ,FINAL_UNPIVOTED.[STAT]
    FROM FINAL_UNPIVOTED
'
SET @sql = REPLACE(@template, '{@sql_unpivot}', @sql_unpivot)

ecc.

Naturalmente, è possibile eseguire questo codice in modo dinamico o creare e SP e puoi scambiare una vista o una tabella che hai creato temporaneamente solo per raccogliere i metadati per qualcosa in linea.

Vedi i commenti riguardanti le funzioni con valori di tabella e la tecnica OUTER APPLY.

Altri suggerimenti

È tardi e qui potrei perdere qualcosa di ovvio, ma una tabella dei mesi con una riga per ogni mese ti aiuterà a farlo?

/* I leave year and month separate so you can use "real" Months or Fiscal Months */

CREATE FUNCTION [dbo].[fn_MonthValueColumns] 
(   
    @year int,
    @month int, 
    @measure int 
)
RETURNS TABLE 
AS
RETURN 
(
    SELECT @year as [Year],
        CASE WHEN @month = 1 THEN @measure ELSE 0 END AS [Jan], 
        CASE WHEN @month = 2 THEN @measure ELSE 0 END AS [Feb], 
        CASE WHEN @month = 3 THEN @measure ELSE 0 END AS [Mar], 
        CASE WHEN @month = 4 THEN @measure ELSE 0 END AS [Apr], 
        CASE WHEN @month = 5 THEN @measure ELSE 0 END AS [May], 
        CASE WHEN @month = 6 THEN @measure ELSE 0 END AS [Jun], 
        CASE WHEN @month = 7 THEN @measure ELSE 0 END AS [Jul], 
        CASE WHEN @month = 8 THEN @measure ELSE 0 END AS [Aug], 
        CASE WHEN @month = 9 THEN @measure ELSE 0 END AS [Sep], 
        CASE WHEN @month = 10 THEN @measure ELSE 0 END AS [Oct], 
        CASE WHEN @month = 11 THEN @measure ELSE 0 END AS [Nov], 
        CASE WHEN @month = 12 THEN @measure ELSE 0 END AS [Dec], 
        @measure AS [Total]
)

  /* 
   use a group by after your own CROSS APPLY to roll-up SUMs for the last 13 fields. 

   this function and a CROSS APPLY against 100000 records ran in 3 seconds.
   for what I am doing, I can live with that performance.
  */

Che ne dici di usare una vista?

Se cerchi sempre lo stesso tavolo / gruppo di tabelle, una vista potrebbe avere senso. CAVEAT: fai attenzione a tali viste, quando usi piccole sezioni di grandi tabelle .. la vista potrebbe impedire all'ottimizzatore di fare il suo lavoro.

Come @Justice menzionato nei commenti, DRY generalmente si riferisce al codice riutilizzabile, che è molto più semplice nella lingua del client SQL non nell'SQL. Se sei aperto a quell'opzione (e, certamente, potresti non esserlo), considera un datamapper come MyBatis . L'estrazione in oggetti può essere eccessiva per ciò che stai cercando, ma la possibilità di creare frammenti SQL e riutilizzarli in query diverse suona come ciò che stai cercando.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top