2011-09-07 105 views
2

我有點卡在寫入存儲過程中。這是事實。我有一個表,下面是例證以逗號分隔的SQL結果

| Name | Score | 

| A  | 10  | 
| A  | 20  | 
| A  | 30  | 
| B  | 20  | 
| B  | 50  | 

和我想要得到的結果從存儲過程

| Name | Scores | 

| A  | 10,20,30 | 
| B  | 20,50  | 

如下是否有可能從一個SQL查詢得到這樣的結果或存儲過程?怎麼樣 ?

回答

0

考慮的意見後修訂:

我建議你閱讀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 
+0

FOR XML PATH('')'是一個非常好的方法,但是當您必須將[N] [VAR] CHAR值與[保留的XML字符串](http://msdn.microsoft.com/zh-cn/庫/ ms145315%28v = sql.90%29.aspx)結果會很奇怪。例如,從「10」,「<20>」,「30」值開始,結果將是「,10,30」。 –

+0

@@ sahlean - 你可以通過使用'.value()'來獲得連接字符串。看看這裏。 http://stackoverflow.com/questions/6603319/concatenate-values-based-on-id/6603735#6603735 –

0

您可以創建一個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貼在我的博客包含此代碼的詳細說明。

+0

我相信這不是一個存儲過程。我怎麼能用你給的腳本? – Andha

+0

@Andha:你說的對 - 這不是存儲過程,更好! * CLR Integration *特性(在SQL Server 2005中引入)允許您創建類似於SUM,MAX或AVG的自定義聚合函數。您需要編譯我提供的代碼,部署程序集並使用'CREATE AGGREGATE'來定義一個新的用戶定義的聚合函數。它需要一點工作,但它是值得的! –

0

在這種情況下,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」是順便把表名,這正是我把它稱爲我的分貝:-)

0

古怪更新方法:

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; 

提及:

  1. 我已經添加在@Test表的主鍵(TestId)。
  2. 請仔細閱讀此article about generating running totals using quirky update method瞭解此方法。