2017-05-22 85 views
0

我想交换列的值以使它们排序。SQL Server:在一行中排序排序

表:

pid | category1 | category2 | category3 
----+-----------+-----------+---------- 
1 | a  | b  | 
2 | b  | a  | 
3 | a  | c  | b 

的结果应该是:

pid | category1 | category2 | category3 
----+-----------+-----------+---------- 
1 | a  | b  | 
2 | a  | b  | 
3 | a  | b  | c 

我的方法是选择列行,责令其在群体的回报新列:

pid | category 
----+--------- 
1 | a 
1 | b 
2 | b 
2 | a 
3 | a 
3 | c 
3 | b 

我找到了pivot函数,但并没有真正理解如何在这种情况下使用它。

+0

你会怎么做一个像一行4'| b | c | '? – SqlZim

+2

所有的最佳选择是将这些数据永久归一化。然后它变得微不足道。当你有像category1,category2这样的列时,你通过重复组违反了1NF。 –

+0

我认为你想unpivot没有透视,这将正常化更好,由@SeanLange建议 – scsimon

回答

1

肖恩朗格是正确的,你应该如何纠正你的数据库模式。

这里是什么让你的结果,直到然后:

使用cross apply(values ...)row_number()沿UNPIVOT您的数据重新排序categorycommon table expression;然后repivoting数据与条件汇总:

;with cte as (
select 
    t.pid 
    , u.category 
    , rn = row_number() over (partition by t.pid order by u.category) 
from t 
    cross apply (values (category1),(category2),(category3)) u(category) 
where nullif(u.category,'') is not null 
) 
select 
    pid 
    , category1 = max(case when rn = 1 then category end) 
    , category2 = max(case when rn = 2 then category end) 
    , category3 = max(case when rn = 3 then category end) 
from cte 
group by pid 

rextester演示:http://rextester.com/GIG22558

回报:

+-----+-----------+-----------+-----------+ 
| pid | category1 | category2 | category3 | 
+-----+-----------+-----------+-----------+ 
| 1 | a   | b   | NULL  | 
| 2 | a   | b   | NULL  | 
| 3 | a   | b   | c   | 
+-----+-----------+-----------+-----------+ 
0

可以转动的,如下下面

select * from (
    select *, RowN = row_number() over(partition by pid order by Category) from Categories 
    ) a 
    pivot (max(category) for RowN in ([1],[2],[3])) p 

输出:

+-----+-----------+-----------+-----------+ 
| Pid | Category1 | Category2 | Category3 | 
+-----+-----------+-----------+-----------+ 
| 1 | a   | b   | NULL  | 
| 2 | a   | b   | NULL  | 
| 3 | a   | b   | c   | 
+-----+-----------+-----------+-----------+ 

因为你可以按照以下使用的列的动态列表:

declare @cols1 varchar(max) 
declare @cols2 varchar(max) 
declare @query nvarchar(max) 

--Row Numbers with tally 
;with c1 as (
select * from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) v(n)) 
,c2 as (select n1.* from c1 n1, c1 n2, c1 n3, c1 n4) 
,RowNumbers as ( 
select top (select max(cnt) from (select cnt = count(*) from Categories group by pid) a) convert(varchar(6), row_number() over (order by (select null))) as RowN 
from c2 n1, c2 n2 
) 
select @cols1 = stuff((select ','+QuoteName(RowN) from RowNumbers group by RowN for xml path('')),1,1,''), 
     @cols2 = stuff((select ',' + QuoteName(RowN) + ' as '+ QuoteName(concat('Category' , RowN)) from RowNumbers group by RowN for xml path('')),1,1,'') 

select @cols1, @cols2 

Set @query = ' Select Pid, '+ @cols2 +' from (' 
Set @query += '  select *, RowN = row_number() over(partition by pid order by Category) from Categories) a ' 
Set @query += ' pivot (max(category) for RowN in (' + @cols1 + ')) p' 

--select @query 
exec sp_executesql @query 
+0

谢谢。伟大的工作,这似乎是一个非常好的解决方案。 –

+0

欢迎...快乐编码 –