2012-08-07 30 views
1

我非常新的SQL,并试图找出了这一点:如何从单列创建多个行 - 规范化表

我有一个叫预算有一年的每个月12列的表,显示该月的预算余额。因此,表看起来像这样:

[Department] [Year] [Month1] [Month2] .... [Month12] 
ABCD   2010 $5000  $5500 ..... $4000 
ABCD   2011 $6000  $6500 ..... $3000 

我所试图做的是要规范化此表,并打破每一行到12行,每行用以下格式的日期字段。我也想要一个[Balance]列来显示当月的价值。所以,规范化的表将如下所示:

[Department] [Date]  [Balance] 
ABCD   20100101  $5000 
ABCD   20100201  $5500 
ABCD   20100301  ..... 
ABCD   .......  ...... 

我试过在同一张表上使用CROSS JOIN,但失败了。我也尝试使用while循环,但也失败了。任何形式的帮助表示赞赏。谢谢!

编辑:我使用SQL Server 2008

+0

如果您在SQL Server 2005或更高版本上,请查看PIVOT和UNPIVOT运算符。 – iruvar 2012-08-07 00:59:08

+0

我在SQL Server 2008上 – Thracian 2012-08-07 01:13:04

回答

9

只是为了好玩这里的CROSS应用的解决方案:

SELECT 
    B.Department, 
    DateAdd(month, (B.Year - 1900) * 12 + M.Mo - 1, 0) [Date], 
    M.Balance 
FROM 
    dbo.Budget B 
    CROSS APPLY (
     VALUES 
     (1, Month1), (2, Month2), (3, Month3), (4, Month4), (5, Month5), (6, Month6), 
     (7, Month7), (8, Month8), (9, Month9), (10, Month10), (11, Month11), (12, Month12) 
    ) M (Mo, Balance); 

这真是不比@Aaron Bertrand的UNPIVOT不同,不使用UNPIVOT。

如果您必须将日期作为字符串,则将字符串放在CROSS APPLY中,如('01', Month1),并将SELECT更改为Convert(char(4), B.Year) + M.Mo

+1

+1巧妙使用申请 – 2012-08-07 01:32:24

+1

这是我值得信赖的朋友! – ErikE 2012-08-07 01:32:58

+0

我认为这可能会成为另一篇不错的博客文章。我敢打赌,许多读者会发现这些新技术中的一些新技术,并且大多数人会惊讶于本页上的哪个解决方案表现最佳。 – 2012-08-07 01:34:21

4
SELECT 
    Department, 
    [Date] = DATEADD(MONTH, CONVERT(INT, SUBSTRING([Month],6,2))-1, 
    DATEADD(YEAR, [Year]-1900, 0)), 
    Balance 
FROM 
    dbo.BUDGET AS b 
    UNPIVOT 
    (
    Balance FOR [Month] IN 
    (
     Month1, Month2, Month3, Month4, 
     Month5, Month6, Month7, Month8, 
     Month9, Month10, Month11, Month12 
    ) 
) AS y 
ORDER BY Department, [Date]; 
+0

我应该多加注意!感谢您的困扰:) – ErikE 2012-08-07 01:56:05

1

这是我要做的事。没有必要让所有人都喜欢它。

select department = b.department , 
     year  = b.year  , 
     month  = m.month  , 
     balance = case m.month 
        when 1 then b.Month1 
        when 2 then b.Month2 
        when 3 then b.Month3 
        when 4 then b.Month4 
        when 5 then b.Month5 
        when 6 then b.Month6 
        when 7 then b.Month7 
        when 8 then b.Month8 
        when 9 then b.Month9 
        when 10 then b.Month10 
        when 11 then b.Month11 
        when 12 then b.Month12 
        else   null 
        end 
from dbo.budget b 
join (   select month = 1 
     union all select month = 2 
     union all select month = 3 
     union all select month = 4 
     union all select month = 5 
     union all select month = 6 
     union all select month = 7 
     union all select month = 8 
     union all select month = 9 
     union all select month = 10 
     union all select month = 11 
     union all select month = 12 
    ) m on 1 = 1 -- a dummy join: we want the cartesian product here so as to expand every row in budget into twelve, one per month of the year. 
+0

我不认为这是关于“花哨”它是关于最小化代码。这样更有效率,但并不漂亮。 :-)你也有一些语法错误 - 什么是'd.'和't.'?该连接只涉及别名'b'和'm'。 – 2012-08-07 01:25:13

+2

当需要交叉连接时,为什么不使用'CROSS JOIN'而不是假条件? – ErikE 2012-08-07 01:34:09