我具有以下粒度的数据:
CityID | Name | Post_Science | Post_Science | Post_Reading | Pre_Reading | Post_Writing | Pre_Writing
123 | Bob | 2.0 | 1.0 | 2.0 | 4.0 | 1.0 | 1.0
我将这些<Post/Pre>_XXXXXX
列称为“标签”。基本上,这些没有“前”或“后”文本的列名称都映射到另一个表中的标签。
我想以某种方式透视数据,以便对于pre
的每组,相同Label的post
和CityID, Name, Label
值位于同一行。所以看起来像这样:
CityID | Name | Pre Category | Post Category | Label
123 | Bob | 1.0 | 2.0 | Science
123 | Bob | 4.0 | 2.0 | Reading
123 | Bob | 1.0 | 1.0 | Writing
Label
通过联接来自单独的表。希望这不会混淆任何人。如果是这样,请暂时忽略该列。
因此,这些类别中有更多类别-例如,科学,阅读和写作只是我所选择的几个类别。
我想到了两种方法来获取这种格式的数据:
CityID, Name, Label
中所有值的长列表。然后解析Label名称,然后将其转到一类的前值和后值分成1行Union
。因此,在一个select
语句中选择所有科学,在另一个select语句中选择所有Reading
,然后将它们合并。大约有50个配对,所以有50个并集语句我在想第一个选项比后者更干净。还有其他选择吗?
答案 0 :(得分:2)
这是不可更改,我强烈建议apply
:
select t.CityId, t.Name, v.*
from t cross apply
(values (t.Post_Science, t.Pre_Science, 'Science'),
(t.Post_Reading, t.Pre_Reading, 'Reading'),
(t.Post_Writing, t.Pre_Writing, 'Writing')
) v(postcategory, precategory, label) ;
UNPIVOT
是做一件事的非常特殊的语法。 APPLY
引入了横向连接,横向连接对于此目的和许多其他目的非常有用。
答案 1 :(得分:1)
很明显,戈登的解决方案性能更高,但是如果您有很多或可变的列,则此选项可以动态取消对数据的使用,而无需实际使用DYNAMIC SQL
示例
Select A.CityID
,A.Name
,PreCat = max(case when Item Like 'Pre%' then Value end)
,PostCat = max(case when Item Like 'Post%' then Value end)
,Label = substring(Item,charindex('_',Item+'_')+1,50)
From YourTable A
Cross Apply ( values (cast((Select A.* for XML RAW) as xml))) B(XMLData)
Cross Apply (
Select Item = xAttr.value('local-name(.)', 'varchar(100)')
,Value = xAttr.value('.','varchar(max)')
From XMLData.nodes('//@*') xNode(xAttr)
Where xAttr.value('local-name(.)','varchar(100)') not in ('CityId','Name','Other-Columns','To-Exclude')
) C
Group By A.CityID
,A.Name
,substring(Item,charindex('_',Item+'_')+1,50)
返回
CityID Name PreCat PostCat Label
123 Bob 4.0 2.0 Reading
123 Bob 1.0 2.0 Science
123 Bob 1.0 1.0 Writing