如何将DRY原则应用于数月透视的SQL语句

时间:2009-01-20 22:23:13

标签: sql sql-server tsql dry pivot

我想知道其他人如何处理这种情况......以及如何将“不要重复自己”(DRY)原则应用于这种情况。

我发现自己经常在T-SQL中PIVOTing或编写CASE语句,将Months列为列。我通常有一些字段包括(1)日期字段和(2)值字段。当我通过ASPX页面或Reporting Services将其呈现给用户时,我需要最后最右边的14列才能使用此模式:

[年],[月],[月],[月],[月],[日],[6月],[月],[月],[月],[月],[月] ,[DEC],[总]

年份是int的年份 并且每个其他字段是该月汇总的值字段([Total]除外,它是该年的总值字段)。

我想找到一种可重用的方法来处理这个问题。打开所有建议(T-SQL / ANSI SQL)

5 个答案:

答案 0 :(得分:3)

这不完全是你想要的,但我已经做了很多重复的UNPIVOT,通常,我会用这种代码生成代码,用某种标准化命名并大量使用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

您可以通过检查表或视图并使用以下内容来编码:

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

并对代码进行模板化:

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)

当然,可以动态运行此代码或创建和SP,您可以换出临时创建的视图或表格,以便为内联内容获取元数据。

请参阅有关表值函数和OUTER APPLY技术的注释。

答案 1 :(得分:1)

现在已经很晚了,我可能会遗漏一些明显的东西,但是每个月有一行的月份表可以帮助你做到这一点吗?

答案 2 :(得分:1)

/* 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.
  */

答案 3 :(得分:0)

使用视图怎么样?

如果您总是使用相同的表/表组,则视图可能有意义。 CAVEAT:当使用大表的一小部分时,请注意这些视图..视图可能会阻止优化器完成它的工作。

答案 4 :(得分:0)

作为评论中提到的@Justice,DRY通常是指可重用的代码,这在SQL客户端语言中比在SQL中更容易。如果您对该选项持开放态度(诚然,您可能不是),请考虑使用MyBatis之类的数据映射器。提取到对象可能对你的后续内容有些过分,但是创建SQL片段并在不同查询中重用它们的能力听起来就像你所追求的那样。