【问题标题】:How to apply the DRY principle to SQL Statements that Pivot Months如何将 DRY 原则应用于枢轴月份的 SQL 语句
【发布时间】:2010-07-07 23:04:14
【问题描述】:

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

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

[年],[一月],[二月],[三月],[四月],[五月],[六月],[七月],[八月],[九月],[十月],[十一月] ,[十二月],[总计]

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

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

【问题讨论】:

  • DRY 代表不要重复自己,不要重复
  • 哈哈。不错的收获。那是一个错字。
  • 我很想尝试一下 Microsoft SQL 2005 中的 XML 数据类型。这样我也许可以使用标量函数来完成一些工作。
  • “不要重复自己”的意思是“构建可重用的抽象”。 SQL 和 Assembly 一样,不是最好的抽象语言。
  • 我同意。但我认为这也意味着“富有成效”……我厌倦了一遍又一遍地写这些“枢轴”。我认为 Cade Roux 让我走上了正确的道路(请参阅他的回答 cmets)。我在 T-SQL 中尽我所能进行抽象,这通常会引导您进入函数。

标签: sql sql-server tsql dry pivot


【解决方案1】:

这并不是你要找的,但我已经做了很多重复的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 技术的 cmets。

【讨论】:

  • +1 - 感谢您的代码。如果没有人有更好的建议,我明天打算看看动态 SQL。我仍然需要弄清楚 [Total] 列...不确定 Pivot 是否会为我执行此操作。
  • 我想我可以尝试一个基于表的函数,它以日期时间为输入,并返回 14 列。然后加入该数据。我必须检查速度影响。
  • 返回单行然后使用 OUTER APPLY 的表值函数可以工作,但对于大型(意味着数千到数百万行)数据集可能会很慢 - 但仍然表现得足够好,我使用它 - 如果必须的话。
  • 甜蜜!我以前从未使用过 APPLY 语法。但这是一个绝佳的机会。从现在开始,每当我必须将“标准”列添加到表的输出中时,我可能会使用它。我有超过 100000 条记录的大约 14 条 CASE 语句。您可以在答案中添加 SEE COMMENTS 短语吗?
  • 我已在此线程上发布了我的表值函数作为另一个答案。
【解决方案2】:

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

【讨论】:

  • 让按月对 count() 进行分组更容易
  • 每个月都有一行?还是一列?我错过了它的作用......我已经有一个 Months 表。
【解决方案3】:
/* 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.
  */

【讨论】:

    【解决方案4】:

    使用视图怎么样?

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

    【讨论】:

    • 我确实使用视图。问题是每当我需要这样做时,我通常会获取一些视图,有时还会获取一个存储过程。试图减少数据库混乱。我想我今天早上要写一些动态SQL并发布它。看看其他人是否可以协作并改进它。
    【解决方案5】:

    正如 cmets 中提到的@Justice,DRY 通常指的是可重用代码,这在您的 SQL 客户端语言中比在 SQL 中更容易。如果您愿意接受该选项(诚然,您可能不会),请考虑使用像 MyBatis 这样的数据映射器。提取到对象可能对您的追求有点过头了,但是创建 SQL sn-ps 并在不同的查询中重用它们的能力听起来就像您追求的那样。

    【讨论】:

      猜你喜欢
      • 2017-07-20
      • 2013-06-06
      • 1970-01-01
      • 2012-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-19
      • 2010-12-01
      相关资源
      最近更新 更多