【问题标题】:Concatenation of fields in different rows连接不同行中的字段
【发布时间】:2011-01-28 13:02:00
【问题描述】:

我陷入了一个无法深入了解的聚合问题。

我有一些数据,最好总结如下

id |phraseId|seqNum|word
=========================
1  |1       |1     |hello
2  |1       |2     |world
3  |2       |1     |black
4  |2       |2     |and
5  |2       |3     |white

我想要一个返回以下数据的查询:

phraseId|completePhrase
========================
1       |hello world
2       |black and white

有人吗?

编辑

我注意到所有提供的解决方案都使用FOR XML PATH。这是什么魔法?

【问题讨论】:

标签: tsql sql-server-2008 aggregate string-concatenation


【解决方案1】:

一种解决方案是使用FOR XML PATH 表达式创建UDF

  • UDF 处理一个 PhraseID 的串联
  • 可以在普通选择中使用

SQL 语句

SELECT  PhraseID, dbo.UDF_ConcatWord(PhraseID)
FROM    Phrases
GROUP BY PhraseID

创建 UDF

CREATE FUNCTION dbo.UDF_ConcatWord(@phraseID INT) RETURNS VARCHAR(8000) AS
BEGIN  
  DECLARE @r VARCHAR(8000)
  SELECT @r = (
    SELECT  word + ', '
    FROM    Phrases
    WHERE   phraseID = @phraseID
    FOR XML PATH('')
  )
  IF LEN(@r) > 0 SET @r = SUBSTRING(@r, 1, LEN(@r)-1)
  RETURN @r
END
GO

编辑

在自己修改了一些链接之后,一个更短的解决方案是

SQL 语句

SELECT  DISTINCT p1.PhraseID
        , STUFF(( SELECT  ' ' + p2.word 
                  FROM    Phrases AS p2 
                  WHERE   p2.PhraseID = p1.PhraseID 
                  FOR XML PATH('')), 1, 1, '') AS completePhrase
FROM      Phrases AS p1
ORDER BY  p1.PhraseID

【讨论】:

  • 我看到了交叉连接答案和你的 UDF,所以我修改了我的父子答案:stackoverflow.com/questions/2342811/… 然后我注意到你的编辑,这和我的几乎一样。我正在检查执行计划以查看是否存在差异,而我的速度较慢,因为我在组合字符串时进行了排序,而您没有。如果数据被乱序插入,你的单词就会出现错误的顺序。向查询添加排序会使 TotalSubtreeCost 慢 0.00000105,这必须是 GROUP BY 与 DISTINCT 之间的差异。
  • 好的。感谢所有的答案。我已经使用了这种形式,但是它们都很棒。谢谢
【解决方案2】:

试试这个:

DECLARE @TableA  table (RowID int, phraseId varchar(5),seqNum int, word varchar(5))

INSERT INTO @TableA VALUES (1,1,1,'hello')
INSERT INTO @TableA VALUES (2,1,2,'world')
INSERT INTO @TableA VALUES (3,2,1,'black')
INSERT INTO @TableA VALUES (4,2,2,'and')
INSERT INTO @TableA VALUES (5,2,3,'white')

SELECT
    c1.phraseId
        ,STUFF(
                 (SELECT
                      ' ' + word
                      FROM @TableA  c2
                      WHERE c2.phraseId=c1.phraseId
                      ORDER BY c1.phraseId, seqNum
                      FOR XML PATH('') 
                 )
                 ,1,1, ''
              ) AS CombinedValue
    FROM @TableA c1
    GROUP BY c1.phraseId
    ORDER BY c1.phraseId

输出:

phraseId CombinedValue
-------- --------------------------
1        hello world
2        black and white

(2 row(s) affected)

【讨论】:

    【解决方案3】:

    我假设您有一个包含每个短语的标题记录的表,这有点作弊。如果缺少,您可以通过从包含单词的表中选择不同的短语 ID 列表来构建它:

    declare @words table
    (id int
    ,phraseId int
    ,seqNum int
    ,word varchar(10)
    )
    
    insert @words
    select 1,1,1,'hello'
    union select 2,1,2,'world'
    union select 3,2,1,'black'
    union select 4,2,2,'and'
    union select 5,2,4,'white'
    
    declare @phrase table
    (phraseId int)
    
    insert @phrase
    select 1
    union select 2
    
    select phraseID
           ,phraseText AS completePhrase
    FROM @phrase AS p
    CROSS APPLY (select word + ' ' as [text()]
                 from @words AS w
                 where w.phraseID = p.phraseID
                 for xml path('')
                ) as phrases (phraseText)
    

    【讨论】:

    • 很好,但是您对标题记录的假设是不必要的。例如: selectphraseID,phraseText AS completePhrase FROM (select distinct phraseId from @words) AS p CROSS APPLY (select word + ' ' as [text()] from @words AS w where w.phraseID = p.phraseID for xml path ('') ) 作为短语 (phraseText)
    • @spender - 这不正是我在回答开头所说的吗?
    • 对不起...错过了。划掉我的最后一条评论!
    【解决方案4】:

    最后我使用了 Lieven 的第二个答案,但发现对于某些字符串组合,FOR XML PATH('') 技巧会导致出现问题:

    declare @phrases table
    (
        id int
        ,phraseId int
        ,seqNum int
        ,word varchar(10)
    )
    
    insert 
        @phrases 
    values
        (1,1,1,'hello'),
        (2,1,2,'world'),
        (3,2,1,'black'),
        (4,2,2,'and'),
        (5,2,3,'white')
    
    SELECT  
        DISTINCT p1.PhraseID, 
        STUFF(
            ( 
                SELECT  
                    ' ' + p2.word 
                FROM    
                    @phrases AS p2 
                WHERE   
                    p2.PhraseID = p1.PhraseID 
                FOR XML PATH('')
            ), 1, 1, '') AS completePhrase
    FROM      
        @phrases AS p1
    ORDER BY  
        p1.PhraseID
    

    工作正常,但如果示例使用在 XML 中使用时需要转义的字符,则会出现问题。例如,通过它运行以下数据:

    insert 
        @words 
    values
        (1,1,1,'hello>'), --notice the less than symbol
        (2,1,2,'world'),
        (3,2,1,'black')
    

    给予

    hello> world
    

    如果源表被声明为无序,则需要order by

    原始查询的一个小修改修复了所有问题:

    SELECT  
        DISTINCT p1.PhraseID, 
        STUFF(
            ( 
                SELECT  
                    ' ' + p2.word 
                FROM    
                    @words AS p2 
                WHERE   
                    p2.PhraseID = p1.PhraseID 
                ORDER BY
                    p2.seqNum  --required
                FOR XML PATH(''),TYPE
            ).value('.','nvarchar(4000)'), 
            1, 
            1, 
            ''
        ) AS completePhrase
    FROM      
        @words AS p1
    ORDER BY  
        p1.PhraseID
    

    (见FOR XML PATH(''): Escaping "special" characters

    【讨论】:

      猜你喜欢
      • 2012-08-07
      • 1970-01-01
      • 1970-01-01
      • 2020-07-30
      • 1970-01-01
      • 1970-01-01
      • 2021-02-12
      • 2013-01-04
      • 2021-02-23
      相关资源
      最近更新 更多