I've encountered what seems to me to be a strange ordering issue when using CONCAT
with a User Defined Table Type.
I've put the SQL below that allows me to reproduce this:
I have the following user type:
CREATE TYPE [dbo].[StringList] AS TABLE
(
[Value] VARCHAR(8000) NOT NULL
)
And the following query to set everything up:
DECLARE @ColumnNames AS TABLE([ColumnName] VARCHAR(MAX))
DECLARE @ValueTable AS [dbo].[StringList] --VariableLineA
--DECLARE @ValueTable AS TABLE([Value] VARCHAR(MAX)) --VariableLineB
INSERT INTO @ColumnNames([ColumnName])
VALUES
('Forename'),
('Surname')
INSERT INTO @ValueTable([Value])
VALUES
('Ellis'), ('Ali')
And this is the query I'm running:
SELECT CONCAT(STRING_AGG(CONCAT([ColumnName], ' - ', [sc].[Value]), ', '), ', ') -- QueryLineA
--CONCAT(STRING_AGG([ColumnName] + ' - ' + [sc].[Value], ', '), ', ') --QueryLineB
FROM @ValueTable AS [sc]
INNER JOIN @ColumnNames ON 1=1
If the VariableLineB
line is uncommented, and the VariableLineA
line is commented out, the following string is returned for both query lines if run individually or together:
Forename - Ellis, Forename - Ali, Surname - Ellis, Surname - Ali,
If the VariableLineA
line is uncommented and the VariableLineB
line is commented out, the following string is returned for QueryLineB
, and from both queries if run together, but not QueryLineA
on its own:
Forename - Ellis, Forename - Ali, Surname - Ellis, Surname - Ali,
When QueryLineA
is run on its own, the following string is returned:
Forename - Ellis, Surname - Ellis, Forename - Ali, Surname - Ali,
Why has the order changed? What am I seeing here, and is there any way to prevent this from happening?
In terms of usage, the values of @ValueTable
would be getting passed into a function, and it's important that the original order (whatever that may be) remains the same.
Best Answer
The order changes because the order of the tables involved in the cross join has changed. In one plan
@ValueTable
is the outer (driving) table, in the other@ColumnNames
is the outer table.Order is never guaranteed unless specified in the query, so if consistent order is important you should add a stable order by clause defining order for both tables inside the
STRING_AGG
definition.