我有一些非规范化的数据,我试图将其移开,希望所有的人都能帮助我找出执行此操作的最佳方法。我已经使用多个并集语句完成了此操作,但是我希望做的是创建一个动态查询,随着向表中添加更多列,该查询可以一遍又一遍地执行此操作。我的数据看起来像这样:(数字列一直到50)
| Code | Desc | Code_0 | Desc_0| Period| 1 | 2 | 3 | 4 | |-------|-------|--------|-------|-------|---------|--------|---------|----------| | NULL | NULL | NULL | NULL | Date |29-Nov-13|6-Dec-13|13-Dec-13| 20-Dec-13| |CTR07 |Risk | P1 | Phase1| P | 0.2 | 0.4 | 0.6 | 1.1 | |CTR07 |Risk | P1 | Phase1| F | 0.2 | 0.4 | 0.6 | 1.1 | |CTR07 |Risk | P1 | Phase1| A | 0.2 | 0.4 | 0.6 | 1.1 | |CTR08 |Oper | P1 | Phase1| P | 0.6 | 0.6 | 0.9 | 2.7 | |CTR08 |Oper | P1 | Phase1| F | 0.6 | 0.6 | 0.9 | 2.7 | |CTR08 |Oper | P1 | Phase1| A | 0.6 | 0.6 | 0.9 | 2.7 |
列标题是最上面的行。如您所见,查看数据时,需要解决一些奇怪的问题。
日期字段开始前的前四个NULL列是一个问题。具有数字标题(1-50)的每一列代表一个星期。问题在于,每个星期在同一列中不仅具有日期字段,而且具有该周的百分比值。我想将其向下旋转,使其看起来像这样:
| Code | Desc |Code_0 |Desc_0 | Period| Date |Percent| |-------|-------|-------|-------|-------|---------|-------| |CTR07 | Risk | P1 | Phase | P | 11/29/13| 0.2 | |CTR07 | Risk | P1 | Phase1| F | 11/29/13| 0.2 | |CTR07 | Risk | P1 | Phase1| A | 11/29/13| 0.2 | |CTR08 | Oper. | P1 | Phase1| P | 11/29/13| 0.6 |
每个星期的日期都在其自己的列中,并将百分比按其各自的日期分组。
由不同的代码,Desc,CODE_0,期间和日期作为键。我想将日期与数字列中的百分比分开,然后将数字列带入按日期连接的自己的列中。如前所述,我已经使用UNION语句静态完成了此操作,但是我想编写某种查询,该查询可以在表扩展时动态地执行。任何帮助将不胜感激。让我知道是否需要任何其他信息,这是我关于StackOverflow的第一个问题,我有两个漂亮的屏幕截图向您展示,但是在这次交流中我还不到10个。仅适用于科幻和幻想。我知道,对吧?
我在联合中用于静态创建底表的代码:
select `Code`, `Desc`, `Code_0`, `Desc_0`, `Period`, (select STR_TO_DATE(`1`, '%d%b%y') from combined_complete where `1` = '29Nov13') as `Date`, `1` as `Percent` from combined_complete where period <> 'Date' union select `Code`, `Desc`, `Code_0`, `Desc_0`, `Period`, (select STR_TO_DATE(`2`, '%d%b%y') from combined_complete where `2` = '06Dec13') as `Date`, `2` from combined_complete where period <> 'Date' union select `Code`, `Desc`, `Code_0`, `Desc_0`, `Period`, (select STR_TO_DATE(`3`, '%d%b%y') from combined_complete where `3` = '13Dec13') as `Date`, `3` from combined_complete where period <> 'Date' union select `Code`, `Desc`, `Code_0`, `Desc_0`, `Period`, (select STR_TO_DATE(`4`, '%d%b%y') from combined_complete where `4` = '20Dec13') as `Date`, `4` from combined_complete where period <> 'Date'
对于这个建议,我创建了一个简单的50行表,称为TransPoser,在MySQL或您的数据库中可能已经有一个整数表,但是您想要类似的东西,将那些编号的列的数字从1转换为N。
TransPoser
然后,使用该表交叉连接到您的非规范化表(我称其为BadTable),但将其限制在第一行。然后使用一组case表达式将pivot这些日期字符串放入一列中。如果需要的话,可以将其转换为适当的日期(我建议这样做,但未包括在内)。
pivot
然后,将这种较小的换位用作主查询中的派生表。
主查询将忽略第一行,但也使用交叉联接将所有原始行强制为50行(在本示例中为4行)。然后将这种笛卡尔积乘回到上面讨论的派生表中以提供日期。然后是另一组case表达式,它们将百分比转换为与日期和各种代码对齐的列。
结果示例(来自示例数据),手动添加了空行:
| N | CODE | DESC | CODE_0 | DESC_0 | THEDATE | PERCENTAGE | |---|-------|------|--------|--------|-----------|------------| | 1 | CTR07 | Risk | P1 | Phase1 | 29-Nov-13 | 0.2 | | 1 | CTR07 | Risk | P1 | Phase1 | 29-Nov-13 | 0.2 | | 1 | CTR07 | Risk | P1 | Phase1 | 29-Nov-13 | 0.2 | | 1 | CTR08 | Oper | P1 | Phase1 | 29-Nov-13 | 0.6 | | 1 | CTR08 | Oper | P1 | Phase1 | 29-Nov-13 | 0.6 | | 1 | CTR08 | Oper | P1 | Phase1 | 29-Nov-13 | 0.6 | | 2 | CTR07 | Risk | P1 | Phase1 | 6-Dec-13 | 0.4 | | 2 | CTR07 | Risk | P1 | Phase1 | 6-Dec-13 | 0.4 | | 2 | CTR07 | Risk | P1 | Phase1 | 6-Dec-13 | 0.4 | | 2 | CTR08 | Oper | P1 | Phase1 | 6-Dec-13 | 0.6 | | 2 | CTR08 | Oper | P1 | Phase1 | 6-Dec-13 | 0.6 | | 2 | CTR08 | Oper | P1 | Phase1 | 6-Dec-13 | 0.6 | | 3 | CTR07 | Risk | P1 | Phase1 | 13-Dec-13 | 0.6 | | 3 | CTR07 | Risk | P1 | Phase1 | 13-Dec-13 | 0.6 | | 3 | CTR07 | Risk | P1 | Phase1 | 13-Dec-13 | 0.6 | | 3 | CTR08 | Oper | P1 | Phase1 | 13-Dec-13 | 0.9 | | 3 | CTR08 | Oper | P1 | Phase1 | 13-Dec-13 | 0.9 | | 3 | CTR08 | Oper | P1 | Phase1 | 13-Dec-13 | 0.9 | | 4 | CTR07 | Risk | P1 | Phase1 | 20-Dec-13 | 1.1 | | 4 | CTR07 | Risk | P1 | Phase1 | 20-Dec-13 | 1.1 | | 4 | CTR07 | Risk | P1 | Phase1 | 20-Dec-13 | 1.1 | | 4 | CTR08 | Oper | P1 | Phase1 | 20-Dec-13 | 2.7 | | 4 | CTR08 | Oper | P1 | Phase1 | 20-Dec-13 | 2.7 | | 4 | CTR08 | Oper | P1 | Phase1 | 20-Dec-13 | 2.7 |
查询:
select n.n , b.Code , b.Desc , b.Code_0 , b.Desc_0 , T.theDate , case when n.n = 1 then `1` when n.n = 2 then `2` when n.n = 3 then `3` when n.n = 4 then `4` /* when n.n = 5 then `5` */ /* when n.n = 50 then `50`*/ end as Percentage from BadTable as B cross join (select N from TransPoser where N < 5) as N inner join ( /* transpose just the date row */ /* join back vis the number given to each row */ select n.n , case when n.n = 1 then `1` when n.n = 2 then `2` when n.n = 3 then `3` when n.n = 4 then `4` /* when n.n = 5 then `5` */ /* when n.n = 50 then `50`*/ end as theDate from BadTable as B cross join (select N from TransPoser where N < 5) as N where b.code is null and b.Period = 'Date' ) as T on N.N = T.N where b.code is NOT null and b.Period <> 'Date' order by n.n , b.code ;
对于以上内容,请参见此SQLFIDDLE
期望恕我直言,结果期望一个完全准备好的可执行文件交付确实不公平- 这是“拉伸友谊”。但是要将上面的查询转换为动态查询并不难。这有点“乏味”,因为语法有点棘手。我对MySQL并没有那么的经验,但这就是我的做法:
set @numcols := 4; set @casevar := ''; set @casevar := ( select group_concat(@casevar ,'when n.n = ' , n.n ,' then `' , n.n ,'`' SEPARATOR ' ') from TransPoser as n where n.n <= @numcols ) ; set @sqlvar := concat( 'SELECT n.n , b.Code , b.Desc , b.Code_0 , b.Desc_0 , T.theDate , CASE ' , @casevar , ' END AS Percentage FROM BadTable AS B CROSS JOIN (SELECT N FROM TransPoser WHERE N <=' , @numcols , ') AS N INNER JOIN ( SELECT n.n , CASE ' , @casevar , ' END AS theDate FROM BadTable AS B CROSS JOIN (SELECT N FROM TransPoser WHERE N <=' , @numcols , ') AS N WHERE b.code IS NULL ' , ' AND b.Period = ''Date'' ) AS T ON N.N = T.N WHERE b.code IS NOT NULL AND b.Period <> ''Date'' ORDER BY n.n , b.code ' ); PREPARE stmt FROM @sqlvar; EXECUTE stmt;
[Demo of the dynamic approach](http://sqlfiddle.com/#!2/d11f7d/2)