If we could apply the values()
method to the main SELECT's only column to extract the ID
values from it, we could use the results to sort the rows, like this:
<column_ref>.values('ID[1]', 'int') ASC
It is actually possible to achieve that. If you assigned the column the name users
and, at the same time, replaced PATH('users')
with PATH('')
, like this:
SELECT (
SELECT B.ID,
B.FirstName,
B.LastName
FROM Users B
WHERE B.UserNum = A.UserNum
FOR XML PATH(''), ELEMENTS, TYPE
) AS users
FROM User_SolutionRole A
WHERE A.Solution = 'SPR'
FOR XML PATH(''), ELEMENTS, TYPE, ROOT('ranks')
;
you would get the same output but the column would now have a reference. That would be your first step towards the goal.
It would seem you'd only need to add an ORDER BY like this:
SELECT (
SELECT B.ID,
B.FirstName,
B.LastName
FROM Users B
WHERE B.UserNum = A.UserNum
FOR XML PATH(''), ELEMENTS, TYPE
) AS users
FROM User_SolutionRole A
WHERE A.Solution = 'SPR'
ORDER BY users.value('ID[1]', 'int') ASC
FOR XML PATH(''), ELEMENTS, TYPE, ROOT('ranks')
;
That wouldn't work, however, not at this point, because the alias assigned in a SELECT clause can only be referenced by the same level ORDER BY clause as is, i.e. it cannot be part of an expression. And so the above would result in a compilation error.
The solution to that would be to assign the name prior to the main SELECT. There are basically two ways:
Using a derived table:
SELECT users FROM ( SELECT ( SELECT B.ID, B.FirstName, B.LastName FROM Users B WHERE B.UserNum = A.UserNum FOR XML PATH(''), ELEMENTS, TYPE ) AS users FROM User_SolutionRole A WHERE A.Solution = 'SPR' ) AS s ORDER BY users.value('ID[1]', 'int') ASC FOR XML PATH(''), ELEMENTS, TYPE, ROOT('ranks') ;
The above uses a "normal" subselect but it would be no difference if you declared it as a CTE instead.
Using
CROSS APPLY
:SELECT x.users FROM User_SolutionRole A CROSS APPLY ( SELECT B.ID, B.FirstName, B.LastName FROM Users B WHERE B.UserNum = A.UserNum FOR XML PATH(''), ELEMENTS, TYPE ) AS x (users) WHERE A.Solution = 'SPR' ORDER BY x.users.value('ID[1]', 'int') ASC FOR XML PATH(''), ELEMENTS, TYPE, ROOT('ranks') ;
My personal preference would be the CROSS APPLY technique as looking simpler and (to me) clearer, but the former method would work equally well.