即使字段为空,stuff()也添加分隔符

时间:2018-10-12 08:44:45

标签: sql-server tsql sql-server-2012

请考虑以下声明:

select stuff((
           select '; ' + ([FIELD_1] + [FIELD_2] + [...] + [FIELD_N])
           from   [TABLE] t1
           where  t1.[ID] = t2.[ID]
           for    xml path ('')
        ),1,1, '')
from    [TABLE] t2

如果该语句将具有相同ID的10条记录串联在一起,但是所有记录都没有值(''或为空,而不是null),则输出为:

; ; ; ; ; ; ; ; ; ;

如果填写了2条记录,我最终会得到

; ; ; AAA; ; ; ; BBB;

在这两种情况下,我分别希望分别是null AAA; BBB

我试图像这样修复它:

select stuff((
           select case when [FIELD_1] <> '' then '; ' + ([FIELD_1]) else '' end
           from   [TABLE] t1
           where  t1.[ID] = t2.[ID]
           for    xml path ('')
        ),1,1, '')
from    [TABLE] t2

这有效,当我仅选择FIELD_1时对我来说足够优雅。但是,当我选择许多隐藏字段(FIELD_1 + ... + FIELD_N)时,这变得非常难看。

我在做什么错? stuff()是否应该作为一个功能来解决我的问题?

2 个答案:

答案 0 :(得分:3)

WHERE子句中添加条件以排除所有值为空字符串的行

select stuff((
           select '; ' + ([FIELD_1] + [FIELD_2] + [...] + [FIELD_N])
           from   [TABLE] t1
           where  t1.[ID] = t2.[ID]
           and    [FIELD_1] + [FIELD_2] + [...] + [FIELD_N] <> ''
           for    xml path ('')
        ),1,1, '')
from    [TABLE] t2

答案 1 :(得分:3)

此答案并非旨在显示一种更好的查询方法,而是旨在解释您所使用的查询。

要解决您的问题,您应该使用his answer中所示的where子句作为松鼠,而不要使用case表达式。选择没有用的东西毫无意义。

我对这种字符串聚合方法感到非常困惑-太多的人在使用它而不了解其每个部分的作用-所以这里有一个简单的解释:

此技术包括三个部分:

  1. 汇总列中的值。 FOR XML PATH('')就是这样做的。 FOR XML PATH('')将返回一个xml字符串,其中xml标记是 查询的列名(或别名)。 (请参见cte_ForXmlAggregated 下面的示例脚本中的“列”)。

  2. 在值之间添加定界符。 ';' +就是这样做的。 它还会从结果中删除列名XML标记- 通过将列名更改为空字符串。空别名在中无效 T-SQL,但是通过将另一个值连接到列,列名称可以 不再使用。
    请参阅样本中的cte_ForXml_WithAnEmptyStringOnlyValuesAggregated 脚本。

  3. 删除字符串开头的多余定界符。那就是 stuff可以。
    请参见示例脚本中的cte_FirstDlimiterRemovedFullQuery

stuff的作用是在指定的index处将一个字符串插入另一个字符串,同时从原始字符串中删除length个字符。如果要插入空字符串,则只需从原始字符串中删除indexlength参数指定的部分。

示例脚本,以说明此字符串聚合技术的不同步骤:

DECLARE @String_Agg AS TABLE 
(
    s varchar(10)
)

INSERT INTO @String_Agg (s) VALUES
('1'), ('2'), ('3')

;WITH cte_ForXml(Aggregated) AS
(
    SELECT s
    FROM @String_Agg
    FOR XML PATH('')
)
, cte_ForXml_WithAnEmptyString(OnlyValuesAggregated) AS
(
    SELECT '' + s
    FROM @String_Agg
    FOR XML PATH('')
) 
, cte_ForXmlWithADelimiter(AggregatedWithADlimiter) AS
(
    SELECT ';' + s
    FROM @String_Agg
    FOR XML PATH('')
), cte_FirstDlimiterRemoved(FullQuery) AS
(
    SELECT STUFF
    (
        (
            SELECT ';' + s
            FROM @String_Agg
            FOR XML PATH('')
        ), 1, 1, ''
    )
)

SELECT Aggregated,  OnlyValuesAggregated, AggregatedWithADlimiter, FullQuery
FROM cte_ForXml
CROSS JOIN cte_ForXml_WithAnEmptyString
CROSS JOIN cte_ForXmlWithADelimiter
CROSS JOIN cte_FirstDlimiterRemoved

结果:

Aggregated                  OnlyValuesAggregated    AggregatedWithADlimiter     FullQuery
<s>1</s><s>2</s><s>3</s>    123                     ;1;2;3                      1;2;3