1. 程式人生 > >SQLServer查詢多行轉換為一行的方法

SQLServer查詢多行轉換為一行的方法

http://www.itfarmer.com.cn/878.html

SQLServer查詢多行轉換為一行的合併問題(FOR XML PATH應用)
以下主講sql多行轉為一行的合併問題,並在sql2000和2005得到驗證
問題描述:將如下形式的資料按id欄位合併value欄位。
id    value
—– ——
1     aa
1     bb
2     aaa
2     bbb
2     ccc
需要得到結果:
id     value
—— ———–
1      aa,bb
2      aaa,bbb,ccc
即:group by id, 求 value 的和(字串相加)

定義表:
create table tb(id int, value varchar(10))
insert into tb values(1, ‘aa’)
insert into tb values(1, ‘bb’)
insert into tb values(2, ‘aaa’)
insert into tb values(2, ‘bbb’)
insert into tb values(2, ‘ccc’)
go
一、sql2000中要用自定義的函式解決
create function dbo.f_str(@id int) returns varchar(100)
as
begin
    declare @str varchar(1000)
    set @str = ”
    select @str = @str + ‘,’ + cast(value as varchar) from tb where id = @id
    set @str = right(@str , len(@str) – 1)
    return @str
end
go
–呼叫函式
select id , value = dbo.f_str(id) from tb group by id
–drop function dbo.f_str
–drop table tb
二、sql2005中可以用FOR XML PATH語句來實現
大家都知道在SQL Server中利用 FOR XML PATH 語句能夠把查詢的資料生成XML資料,下面是它的一些應用示例。
DECLARE @TempTable table(UserID int , UserName nvarchar(50));
insert into @TempTable (UserID,UserName) values (1,’a’)
insert into @TempTable (UserID,UserName) values (2,’b’)
select UserID,UserName from @TempTable FOR XML PATH
執行這段指令碼,將生成如下結果:
<row>
  <UserID>1</UserID>
  <UserName>a</UserName>
</row>
<row>
  <UserID>2</UserID>
  <UserName>b</UserName>
</row>
可以看到兩行資料生成了兩個節點,修改一下PATH的引數:
select UserID,UserName from @TempTable FOR XML PATH(‘test’)
執行上述指令碼,將生成如下的結果:
<test>
  <UserID>1</UserID>
  <UserName>a</UserName>
</test>
<test>
  <UserID>2</UserID>
  <UserName>b</UserName>
</test>
其實PATH() 括號內的引數是控制節點名稱的,那如果是空字串(不是沒有引數)會是什麼結果呢?
select UserID,UserName from @TempTable FOR XML PATH(”)
執行上面這段指令碼將生成結果:
<UserID>1</UserID>
<UserName>a</UserName>
<UserID>2</UserID>
<UserName>b</UserName>
這樣就不顯示上級節點了,在PATH模式中,列名或列別名被作為XPath表示式來處理,也就是說,是列的名字,試一下不給指定列名和別名會是怎麼樣?
select CAST(UserID AS varchar) + ”,UserName + ” from @TempTable FOR XML PATH(”)
執行上面這句將生成結果
1a2b
所有資料都生成一行,而且沒有連線字元,還可以再變化一下:
select CAST(UserID AS varchar) + ‘,’,UserName + ”,';’ from @TempTable FOR XML PATH(”)
生成結果
1,a;2,b;
也就是說可以通過控制引數來生成自己想要的結果,例如:
select ‘{‘ + CAST(UserID AS varchar) + ‘,’,'”‘ +UserName + ‘”‘,’}’ from @TempTable FOR XML PATH(”)
生成結果
{1,”a”}{2,”b”}
還可以生成其他格式,大家可以根據自己需要的格式進行組合。
下面是一個數據統計的應用,希望大家可以通過下面的例項想到更多的應用
DECLARE @T1 table(UserID int , UserName nvarchar(50),CityName nvarchar(50));
insert into @T1 (UserID,UserName,CityName) values (1,’a’,’上海’)
insert into @T1 (UserID,UserName,CityName) values (2,’b’,’北京’)
insert into @T1 (UserID,UserName,CityName) values (3,’c’,’上海’)
insert into @T1 (UserID,UserName,CityName) values (4,’d’,’北京’)
insert into @T1 (UserID,UserName,CityName) values (5,’e’,’上海’)
SELECT B.CityName,LEFT(UserList,LEN(UserList)-1) FROM (
SELECT CityName,
    (SELECT UserName+’,’ FROM @T1 WHERE CityName=A.CityName  FOR XML PATH(”)) AS UserList
FROM @T1 A
GROUP BY CityName
) B
生成結果(每個城市的使用者名稱)
北京 b,d
上海 a,c,e
回到上面的例子就是執行如下語句:
select id, [value] = stuff((select ‘,’ + [value] from tb t where id = tb.id for xml path(”)) , 1 , 1 , ”)
from tb
group by id
–drop table tb
(STUFF函式用法
1、作用:刪除指定長度的字元,並在指定的起點處插入另一組字元。
2、語法:STUFF ( character_expression , start , length ,character_expression )
3、示例:在第一個字串abcdef中刪除從第2個位置(字元b)開始的3個字元,然後在刪除的起始位置插入第二個字串ijklmn,從而建立並返回一個新字串
SELECT STUFF(‘abcdef’, 2, 3, ‘ijklmn’)
GO
下面是結果集:aijklmnef
4、引數:character_expression,一個字元資料表示式。character_expression可以是常量、變數,也可以是字元列或二進位制資料列。
start,一個整數值,指定刪除和插入的開始位置。如果start或length為負,則返回空字串。如果start比第一個character_expression長,則返回空字串。start可以是bigint型別。
length,一個整數,指定要刪除的字元數。如果length比第一個character_expression長,則最多刪除到最後一個 character_expression中的最後一個字元。length可以是bigint型別。
5、返回型別:如果 character_expression 是受支援的字元資料型別,則返回字元資料。如果 character_expression 是一個受支援的 binary 資料型別,則返回二進位制資料。
6、備註:如果結果值大於返回型別支援的最大值,則產生錯誤。)
三、使用遊標來合併資料,這個方法無版本限制
declare @t table(id int,value varchar(100))–定義結果集表變數
–定義遊標並進行合併處理
declare my_cursor cursor local for
select id, value from tb
declare @id_old int, @id int, @value varchar(10), @s varchar(100)
open my_cursor
fetch my_cursor into @id, @value
select @id_old = @id, @s=”
while @@FETCH_STATUS = 0
begin
    if @id = @id_old
       select @s = @s + ‘,’ + cast(@value as varchar)
    else
      begin
        insert @t values(@id_old , stuff(@s,1,1,”))
        select @s = ‘,’ + cast(@value as varchar) , @id_old = @id
      end
    fetch my_cursor into @id , @value
END
insert @t values(@id_old , stuff(@s,1,1,”))
close my_cursor
deallocate my_cursor

select * from @t
–drop table tb

這個一般常見用於資料統計,平常用的不多。