Strange TSQL behavior with COALESCE when using Ord

2019-03-01 17:29发布

问题:

This question already has an answer here:

  • nvarchar concatenation / index / nvarchar(max) inexplicable behavior 2 answers

I'm having some very strange behavior with coalesce. When I don't specify a return amount (TOP (50)) I'm only getting a single last result, but if I remove the "Order By" it works... Examples below

DECLARE @result varchar(MAX)
SELECT @result = COALESCE(@result + ',', '') + [Title]
FROM Episodes WHERE [SeriesID] = '1480684' AND [Season] = '1' Order by [Episode] ASC
SELECT @result

Will only return a single last result:

The Shiva Bowl

But If I specifiy a max return amount (only adding TOP (50) to same statement)

DECLARE @result varchar(MAX)
SELECT TOP(50) @result = COALESCE(@result + ',', '') + [Title]
FROM Episodes WHERE [SeriesID] = '1480684' AND [Season] = '1' Order by [Episode] ASC
SELECT @result

I get all the results in proper order

The Draft,The Bounce Test,Sunday at Ruxin's,Mr. McGibblets,The Usual Bet,The Shiva Bowl

The culprit seems to be the [Title] column as if I return a different column it seems to be working without specifying a return limit. FYI [Title] is a VARCHAR(MAX) NOT NULL column.

Any insight what could be causing this? I really don't want to set a limit, but it's the only way it's returning all the data at the moment... Thanks

回答1:

You cannot depend on concatenation like:

SELECT @result = COALESCE(@result + ',', '') + [Title]
FROM Episodes
...

Execution Plan and Results of Aggregate Concatenation Queries Depend Upon Expression Location

Example dependent on CTE/temp table/execution plan you will get different results:

SqlFiddleDemo

DECLARE @text VARCHAR(MAX) = ''
       ,@text2 VARCHAR(MAX) = '';

SELECT CAST(ROW_NUMBER() OVER (ORDER BY name) AS INT) AS number 
INTO #numbers 
FROM master..spt_values 


;WITH numbers (number)
AS
(
    SELECT CAST(ROW_NUMBER() OVER (ORDER BY name) AS INT) AS number
    FROM master..spt_values 
),a AS
(
    SELECT number FROM numbers WHERE number < 10
)
SELECT      @text = @text + LTRIM(STR(a.number))
FROM        a
ORDER BY    a.number DESC


;WITH numbers (number)
AS
(
    SELECT number FROM #numbers
),
a
AS
(
    SELECT number FROM numbers WHERE number < 10
)
SELECT      @text2 = @text2 + LTRIM(STR(a.number))
FROM        a
ORDER BY    a.number DESC

SELECT @text, @text2;

I made sample and your first query is working SqlFiddleDemo. But your solution is higly dependent on execution plan.

Use XML + STUFF for concatenation instead.