2017-05-22 120 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

歡迎...快樂編碼 –