stuff() adds separator even when fields are empty

☆樱花仙子☆ 提交于 2019-12-25 03:49:59

问题


Please consider the following statement:

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

If the statement concatenates 10 records with the same ID but all records have no value ('', or empty, instead of null), the output is:

; ; ; ; ; ; ; ; ; ;

If 2 records are filled, I end up with

; ; ; AAA; ; ; ; BBB;

What I would want in these 2 cases is null, respectively AAA; BBB

I tried fixing it like this:

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

This works, and is elegantly enough for me when I'm only selecting FIELD_1. But when I'm selecting many concatinated fields (FIELD_1 + ... + FIELD_N), this becomes quite ugly fast.

What am I doing wrong? Isn't stuff() supposed to take care of my problem as a function?


回答1:


add a condition to the WHERE clause to exclude rows with all value empty string

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



回答2:


This answer is not intended to show a better way to do the query - it is intended to explain the query you are using.

To solve your problem you should use the where clause as squirrel demonstrated in his answer, instead of using the case expression. No point of selecting things you have no use for.

I see a lot of confusion about this method of string aggregation - too many people are using it without understanding what each part of it does - so here is a simple explanation:

There are three parts to this technique:

  1. aggregate the values in the columns. That's what the FOR XML PATH('') does. The FOR XML PATH('') will return an xml string where the xml tags are the column name (or aliases) of the query. (see cte_ForXml and Aggregated column in the sample script below).

  2. Add a delimiter between the values. That's what the ';' + does. It also removes the column name XML tags from the results - by changing the column name to an empty string. Empty aliases are invalid in T-SQL, but by concatenating another value to the column the column name can no longer be used.
    See cte_ForXml_WithAnEmptyString and OnlyValuesAggregated on the sample script.

  3. Remove the extra delimiter at the beginning of the string. That's what the stuff does.
    See cte_FirstDlimiterRemoved and FullQuery in the sample script.

What stuff does is to insert a string into another string, at a specified index, while removing length charecters from the original string. If you are inserting an empty string, you are simply removing the part specified by the index and length parameters from the original string.

Sample script to illustrate the different steps of this string aggregation technique:

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

Results:

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


来源:https://stackoverflow.com/questions/52775632/stuff-adds-separator-even-when-fields-are-empty

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!