HiveBrain v1.2.0
Get Started
← Back to all entries
patternsqlMinor

Order by 1 shortens the result set to one row

Submitted by: @import:stackexchange-dba··
0
Viewed 0 times
resultordertheonerowsetshortens

Problem

I'm running the following query:

DECLARE @sql NVARCHAR(max) = N''
SELECT @sql += N' EXEC dbo.DoSomething @Tablename = ''' + SCHEMA_NAME(T.schema_id) + N'.' + T.name + N''''
--SELECT * 
FROM sys.tables T
INNER JOIN sys.indexes I ON I.object_id = T.object_id
ORDER by 1 
PRINT  @sql


But I'm only getting one line printed.
If I take out the ORDER by 1 line, or replace it with

ORDER BY t.name then I get the expected result.

Is this a bug in SQL Server?

I've tried it on SQL SErver 2016 SP1 and 2014 Sp2-Cu1.

Solution

Is this a bug in SQL Server?

No.

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

The correct behavior for an aggregate concatenation query is
undefined.

See also this excellent answer by Martin Smith over at stackoverflow.
nvarchar concatenation / index / nvarchar(max) inexplicable behavior

Sometimes it works and sometimes it does not work and that is dependent on the execution plan. The magic works if the compute scalar doing the concatenation is positioned as the first operator below the select operator.

declare @T table(Name nvarchar(5));
insert into @T values (N'aaaa3'),(N'bbbb2'),(N'cccc1');
declare @S nvarchar(18) = N'';

select @S += ',' + T.Name
from @T as T;


But if you are unlucky you could end up with a different plan. This one has a sort operator between the select and the compute scalar.

select @S += ',' + T.Name
from @T as T
order by reverse(T.Name);


The best way to solve this (in my opinion) is to use for xml path to do the concatenation instead.

select @S  = (
             select ',' + T.Name 
             from @T as T
             order by reverse(T.Name)
             for xml path(''), type
             ).value('text()[1]', 'nvarchar(max)');


Some version of SQL Server in the future will have a built in string aggregate function STRING_AGG (Transact-SQL).

Code Snippets

declare @T table(Name nvarchar(5));
insert into @T values (N'aaaa3'),(N'bbbb2'),(N'cccc1');
declare @S nvarchar(18) = N'';

select @S += ',' + T.Name
from @T as T;
select @S += ',' + T.Name
from @T as T
order by reverse(T.Name);
select @S  = (
             select ',' + T.Name 
             from @T as T
             order by reverse(T.Name)
             for xml path(''), type
             ).value('text()[1]', 'nvarchar(max)');

Context

StackExchange Database Administrators Q#156347, answer score: 7

Revisions (0)

No revisions yet.