如何将一行中的所有值连接成一个字符串?

4
假设我有一行数据,存储如下所示:
    ------------------------
   | Col 1 | Col 2 | Col 3  |
   |------------------------|
   |  Foo  |  Bar  | Foobar |

我该如何将这些内容连接成一个字符串,就像下面这样?
Foo-Bar-Foobar

这个表格的列标题(和列标题的数量)是未知的,因此按列名选择不是一个选项。请注意,我不是在尝试连接列中的值列表,我正在尝试连接存储在一行中的值。我也更喜欢避免使用数据透视表,因为我将处理大量数据,并且不想影响性能。

1
@Chapman 您需要表格的所有列吗?如果不是,您会保留哪些列?字符串的顺序将如何确定(例如,您如何知道要制作Foo-Bar-Foobar而不是Foobar_foo_bar)? - George Menoutis
你使用的是哪个版本的SQL Server? - Eric Brandt
顺序无关紧要,所有列都应包含在字符串中。我们正在运行的SQL服务器版本是11.0.6260。 - J. Chapman
4个回答

2
在这种情况下,我非常欣赏XML在处理通用集合方面的强大能力:
SELECT STUFF(b.query('
                        for $element in ./*
                        return
                        <x>;{$element/text()}</x>
                       ').value('.','nvarchar(max)'),1,1,'')
FROM 
(   
    SELECT TOP 3 * FROM sys.objects o FOR XML PATH('row'),ELEMENTS XSINIL,TYPE
) A(a)
CROSS APPLY a.nodes('/row') B(b);

结果

sysrscols;3;4;0;S ;SYSTEM_TABLE;2017-08-22T19:38:02.860;2017-08-22T19:38:02.867;1;0;0
sysrowsets;5;4;0;S ;SYSTEM_TABLE;2009-04-13T12:59:05.513;2017-08-22T19:38:03.197;1;0;0
sysclones;6;4;0;S ;SYSTEM_TABLE;2017-08-22T19:38:03.113;2017-08-22T19:38:03.120;1;0;0

备注

一些需要提及的事项

  • 我使用分号;作为分隔符,因为连字符-可能会在包含连字符的值(例如DATE)中出现错误。
  • 我使用sys.objects中的TOP 3创建一个易于使用的样例。
  • 感谢Zohard Peled,我添加了ELEMENTS XSINIL来强制引擎不忽略NULL值。

在2016年之前的版本中创建JSON

您可以尝试使用以下方法在2016年之前的版本中创建JSON字符串

SELECT '{' 
      + STUFF(b.query('
                        for $element in ./*
                        return
                        <x>,"{local-name($element)}":"{$element/text()}"</x>
                       ').value('.','nvarchar(max)'),1,1,'')
      + '}'
FROM 
(   
    SELECT TOP 3 * FROM sys.objects o FOR XML PATH('row'),TYPE
) A(a)
CROSS APPLY a.nodes('/row') B(b);

结果

{"name":"sysrscols","object_id":"3","schema_id":"4","parent_object_id":"0","type":"S ","type_desc":"SYSTEM_TABLE","create_date":"2017-08-22T19:38:02.860","modify_date":"2017-08-22T19:38:02.867","is_ms_shipped":"1","is_published":"0","is_schema_published":"0"}
{"name":"sysrowsets","object_id":"5","schema_id":"4","parent_object_id":"0","type":"S ","type_desc":"SYSTEM_TABLE","create_date":"2009-04-13T12:59:05.513","modify_date":"2017-08-22T19:38:03.197","is_ms_shipped":"1","is_published":"0","is_schema_published":"0"}
{"name":"sysclones","object_id":"6","schema_id":"4","parent_object_id":"0","type":"S ","type_desc":"SYSTEM_TABLE","create_date":"2017-08-22T19:38:03.113","modify_date":"2017-08-22T19:38:03.120","is_ms_shipped":"1","is_published":"0","is_schema_published":"0"}

提示

您可以在此查询中添加ELEMENTS XSINIL。这取决于您是否希望忽略NULL,或者您希望将它们包含在"SomeColumn":""中。


这个会如何处理空值? - Zohar Peled
@ZoharPeled,这很简单,只需添加ELEMENTS XSINIL即可。这将强制引擎生成一个空元素,导致CSV中的空值(重复分号)。 - Shnugo
好的,我已经为你的答案点赞了。我原本打算使用基于information_schema.columns创建SQL的繁琐解决方案,但这个方法更加优雅。 - Zohar Peled
谢谢您的回答,这对我的目的非常有效,但有一个例外:格式。我的一些数据列包含小数0。当提取此值时,它被表示为“.000000000000000e+000”。您有什么想法可以防止以这种方式格式化它? - J. Chapman
@J.Chapman XML 不知道类型,它将返回内容“原样”。当然,可以添加许多“高度复杂”的业务逻辑,但这将需要相当大的工作量。您可以将模式包含在 XML 中,并在 XQuery 中使用内部引用,或者从 INFORMATION_SCHEMA.COLUMNS 创建一些语句。或者,您可以只是添加一个基于字符串的文本替换,将 '.000000000000000e+000' 替换为 '0' - Shnugo

1
我使用UnitE,这是我用来从person表动态选择列的方法。
INFORMATION_SCHEMA.COLUMNS存储表的列列表,SELECT语句基于此构建。
Declare @Columns NVARCHAR(MAX)
Declare @Table varchar(15) = 'capd_person'

SELECT @Columns=COALESCE(@Columns + ',', '') + COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE (TABLE_NAME=@Table )  

EXEC('SELECT DISTINCT ' + @Columns + ' FROM ' + @Table)

您需要根据自己的需求更改EXEC命令,如前所述使用CONCAT。

使用 SELECT @SomeVariable = @SomeVariable + ...古怪更新 可能会产生意外的副作用... 有更好的方法来连接一系列值。通常人们使用 FOR XML,在2016年及之后的版本中还有 STRING_AGG()... - Shnugo
@Shrugo,我在教育公共部门工作,我们仍在使用2008 R2;-) - JonTout
1
FOR XML与本地XML支持在v2005中被引入;-) 如果您愿意,可以尝试我的答案中的查询。这应该在您的系统中无任何问题... - Shnugo

1

只需执行SELECT CONCAT(col1,col2,col3) FROM table

但如果你想让它更整洁

使用:SELECT CONCAT(col1,'-',col2,'-',col3) FROM table

这里找到更多帮助。


3
正如 OP 所说,列名和数量是未知的。 - George Menoutis

0

JonTout 的答案的改进版本:

    Declare @Columns NVARCHAR(MAX)
    Declare @Table varchar(15) = 'TableName'

    SELECT @Columns=COALESCE(@Columns + '+', '') +'CONVERT(varchar(max),ISNULL('+ COLUMN_NAME+',''''))+''-'''
    FROM INFORMATION_SCHEMA.COLUMNS
    WHERE (TABLE_NAME=@Table ) 


    EXEC('SELECT ' + @Columns + ' FROM ' + @Table)

你可能想阅读一下我在John Tout的答案下关于“古怪更新”的评论... - Shnugo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接