我有點卡在寫入存儲過程中。這是事實。我有一個表,下面是例證以逗號分隔的SQL結果
| Name | Score |
| A | 10 |
| A | 20 |
| A | 30 |
| B | 20 |
| B | 50 |
和我想要得到的結果從存儲過程
| Name | Scores |
| A | 10,20,30 |
| B | 20,50 |
如下是否有可能從一個SQL查詢得到這樣的結果或存儲過程?怎麼樣 ?
我有點卡在寫入存儲過程中。這是事實。我有一個表,下面是例證以逗號分隔的SQL結果
| Name | Score |
| A | 10 |
| A | 20 |
| A | 30 |
| B | 20 |
| B | 50 |
和我想要得到的結果從存儲過程
| Name | Scores |
| A | 10,20,30 |
| B | 20,50 |
如下是否有可能從一個SQL查詢得到這樣的結果或存儲過程?怎麼樣 ?
考慮的意見後修訂:
我建議你閱讀Rob Farley's優秀的博客後Handling special characters with FOR XML PATH('')。他的解決方案將允許您的分組ID的字符串連接,無需concern for fields that might have special characters。
DECLARE @t TABLE (Name CHAR(1), Score INT)
INSERT @t VALUES
('A', 10),
('A', 20),
('A', 30),
('B', 20),
('B', 50)
SELECT
STUFF(
(SELECT ', ' + CONVERT(VARCHAR(10), Score)
FROM @t
WHERE Name = t.Name
ORDER BY Score
FOR XML PATH(''),
TYPE).value('(./text())[1]','varchar(max)'),
1, 2, '') AS Score
FROM @t t
GROUP BY Name
您可以創建一個CLR user-defined aggregate:
using System;
using System.Data;
using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;
using System.IO;
using System.Text;
[Serializable()]
[SqlUserDefinedAggregate(
Format.UserDefined,
IsInvariantToNulls=true,
IsInvariantToDuplicates=false,
IsInvariantToOrder=false,
MaxByteSize=8000)]
public class Concat : IBinarySerialize
{
#region Private fields
private string separator;
private StringBuilder intermediateResult;
#endregion
#region IBinarySerialize members
public void Read(BinaryReader r)
{
this.intermediateResult = new StringBuilder(r.ReadString());
}
public void Write(BinaryWriter w)
{
w.Write(this.intermediateResult.ToString());
}
#endregion
#region Aggregation contract methods
public void Init()
{
this.separator = ", ";
this.intermediateResult = new StringBuilder();
}
public void Accumulate(SqlString pValue)
{
if (pValue.IsNull)
{
return;
}
if (this.intermediateResult.Length > 0)
{
this.intermediateResult.Append(this.separator);
}
this.intermediateResult.Append(pValue.Value);
}
public void Merge(Concat pOtherAggregate)
{
this.intermediateResult.Append(pOtherAggregate.intermediateResult);
}
public SqlString Terminate()
{
return this.intermediateResult.ToString();
}
#endregion
}
,並用它在查詢像任何其他聚合函數:
SELECT Name, dbo.Concat(Score) AS Scores
FROM dbo.Table
GROUP BY Name
文章A SQL CLR user-defined aggregate - notes on creating and debugging貼在我的博客包含此代碼的詳細說明。
我相信這不是一個存儲過程。我怎麼能用你給的腳本? – Andha
@Andha:你說的對 - 這不是存儲過程,更好! * CLR Integration *特性(在SQL Server 2005中引入)允許您創建類似於SUM,MAX或AVG的自定義聚合函數。您需要編譯我提供的代碼,部署程序集並使用'CREATE AGGREGATE'來定義一個新的用戶定義的聚合函數。它需要一點工作,但它是值得的! –
在這種情況下,COALESCE是你的朋友。我不是COALESCE的專家,我只是知道它的工作原理,所以如果你想深入挖掘,你可能想查看它。
下面的代碼片段會一次給你一行,給出在@Name中尋找的名字,我會把它變成SQL服務器中的函數,然後在你的上層SP中的某處調用該函數,word但謹慎:如果您的結果集中有NULL值,它會導致您得到不正確的結果。
DECLARE @Name varchar
SET @Name = 'A'
DECLARE @Row varchar(max)
SELECT
@Row = COALESCE(@Row + ', ','') + CAST(Score AS varchar)
FROM
sotest2
WHERE
name = @Name
SELECT @Name,@Row
「sotest2」是順便把表名,這正是我把它稱爲我的分貝:-)
古怪更新方法:
DECLARE @DistinctName TABLE
(
Name VARCHAR(10) PRIMARY KEY
);
INSERT @DistinctName (Name)
VALUES ('A'),('B');
DECLARE @Test TABLE
(
TestId INT IDENTITY(1,1) PRIMARY KEY
,Name VARCHAR(10) NOT NULL
,Score INT NOT NULL
,Result VARCHAR(1000) NOT NULL DEFAULT ''
);
INSERT @Test (Name, Score)
VALUES ('A',10),('A',20),('A',30),('B',40),('B',50);
DECLARE @OldName VARCHAR(10) = ''
,@IsNewGroup BIT = 1
,@Concat VARCHAR(1000);
WITH SourceCTE
AS
(
SELECT TOP(1000) t.*
FROM @Test t
ORDER BY t.Name
)
UPDATE SourceCTE
SET @IsNewGroup = CASE WHEN @OldName <> Name THEN 1 ELSE 0 END
,@OldName = Name
,@Concat = Result = CASE WHEN @IsNewGroup = 1 THEN '' ELSE @Concat END + ',' + CAST(Score AS VARCHAR(10))
--OUTPUT inserted.Name, inserted.TestId , inserted.Result
SELECT dn.Name
,SUBSTRING(ca.Result,2,1000) Result
FROM @DistinctName dn
CROSS APPLY
(
SELECT TOP(1) Result
FROM @Test t
WHERE t.Name = dn.Name
ORDER BY t.TestId DESC
) ca;
提及:
FOR XML PATH('')'是一個非常好的方法,但是當您必須將[N] [VAR] CHAR值與[保留的XML字符串](http://msdn.microsoft.com/zh-cn/庫/ ms145315%28v = sql.90%29.aspx)結果會很奇怪。例如,從「10」,「<20>」,「30」值開始,結果將是「,10,30」。 –
@@ sahlean - 你可以通過使用'.value()'來獲得連接字符串。看看這裏。 http://stackoverflow.com/questions/6603319/concatenate-values-based-on-id/6603735#6603735 –