在項目開發中,有時會碰到將列記錄合並為一行的情況,例如根據地區將人員姓名合並,或根據拼音首字母合並城市等,下面就以根據地區將人員姓名合並為例,詳細講一下合並的方法。
首先,先建一個表,並添加一些數據,建表代碼如下:
If OBJECT_ID(N'Demo') Is Not Null
Begin
Drop Table Demo
End
Else
Begin
Create Table Demo(
Area nvarchar(30),
Name nvarchar(20))Insert Into Demo(Area,Name)
Values(N'北京',N'張三'),
(N'上海',N'李四'),
(N'深圳',N'王五'),
(N'深圳',N'錢六'),
(N'北京',N'趙七'),
(N'北京','Tom'),
(N'上海','Amy'),
(N'北京','Joe'),
(N'深圳','Leo')
End
Go
建完后查詢一下,可見表中數據如下:
如果僅將Name列合並,不遵循任何條件的話,我們可以采用兩種方法,第一種就是采用FOR XML PATH方式,代碼如下:
SELECT ','+Name FROM dbo.Demo FOR XML PATH('')
運行結果如下:
關於FOR XML PATH的詳細介紹可參考MSDN:搭配 FOR XML 使用 PATH 模式
第二種方法就是定義一個變量用來裝載查詢的結果,代碼如下:
Declare @NameCollection nvarchar(500)
Select @NameCollection=ISNULL(@NameCollection+',','')+Name From dbo.Demo
Select @NameCollection as NameCollection
運行結果如下:
加了ISNULL是因為最開始變量@NameCollection為NULL,為了避免“張三”前多一個逗號(“,”)而采用的替換。
上面講了在無條件的情況下合並一列,但是在項目中幾乎不會遇到這樣的情況,一般都是根據某一列來合並另一列的數據,例如我們現在要根據Area將Name合並,得到這樣的結果:
有了上面的基礎,要合並成這樣的數據就容易了,我們只需要針對Area列采用聚合GROUP BY或取不重復值DISTINCT,然后根據Area列合並Name列,有了思路,下面就來說說如何實現,首先還是采用FOR XML PATH方式,結合自連接,首先先按Area列對Name列進行合並,代碼如下:
SELECT Area,
(SELECT ','+Name FROM dbo.Demo WHERE Area = t.Area FOR XML PATH(''))
AS NameCollection FROM dbo.Demo AS t
運行結果如下:
現在有兩點還沒實現,第一是結果重復了,第二是NameCollection列最開始都多了一個逗號,先去掉逗號,采用STUFF 函數來進行替換,代碼修改如下:
SELECT Area,
STUFF((SELECT ','+Name FROM dbo.Demo WHERE Area = t.Area FOR XML PATH('')),1,1,'')
AS NameCollection FROM dbo.Demo AS t
現在運行后結果如下:
下面就剩下去掉重復數據了,分別采用GROUP BY和DISTINCT,代碼如下:
SELECT DISTINCT Area,
STUFF((SELECT ','+Name FROM dbo.Demo WHERE Area = t.Area FOR XML PATH('')),1,1,'')
AS NameCollection FROM dbo.Demo AS t
SELECT Area,
STUFF((SELECT ','+Name FROM dbo.Demo WHERE Area = t.Area FOR XML PATH('')),1,1,'')
AS NameCollection FROM dbo.Demo AS t GROUP BY Area
關於STUFF函數可以參考MSDN介紹:STUFF函數
運行結果即為最終我們需要的結果,最開始在上面講到了一種用變量來裝載查詢結果實現合並一列的方法,下面詳細介紹如何采用上述方法來實現我們的需求,我們可以根據上面的方法建一個函數,傳入一個Area參數,根據Area來進行合並,返回合並值,函數如下:
CREATE FUNCTION MergeByColumn
(
-- Add the parameters for the function here
@Area nvarchar(30)
)
RETURNS nvarchar(500)
AS
BEGIN
-- Declare the return variable here
DECLARE @NC nvarchar(500)-- Add the T-SQL statements to compute the return value here
SELECT @NC=ISNULL(@NC+',','')+Name FROM dbo.Demo WHERE Area=@Area-- Return the result of the function
RETURN @NCEND
GO
建好后測試下,以傳入參數為“北京”為例,運行如下代碼:
SELECT dbo.MergeByColumn('北京') AS NameCollection
得到結果如下:
現在只需將Area列也加入查詢即可,修改代碼如下:
SELECT Area,dbo.MergeByColumn(Area) AS NameCollection From dbo.Demo
現在也得到了重復的結果,如下:
去重復同樣可以用GROUP BY和DISTINCT,代碼如下,即可以得到我們最終的結果:
SELECT DISTINCT Area,dbo.MergeByColumn(Area) AS NameCollection From dbo.Demo
SELECT Area,dbo.MergeByColumn(Area) AS NameCollection From dbo.Demo GROUP BY Area








