Sort data before concatenating using STUFF FOR XML

南笙酒味 提交于 2019-12-23 12:16:23

问题


I have the following query that I am using for an SSRS Report:

SELECT  ROW_NUMBER() OVER ( ORDER BY Judge.EventJudgeID ) AS JudgeRow ,
        Judge.EventID ,
        Judge.Judge_PersonID ,
        STUFF(( SELECT DISTINCT
                        ',' + CAST(Fights.FightNumber AS VARCHAR(MAX)) AS [text()]
                FROM    dbo.tblFights Fights ,
                        dbo.tblFightJudge FRJudge
                WHERE   Fights.FightID = FRJudge.fightid
                        AND ( Judge.Judge_PersonID = FRJudge.judge1id
                              OR Judge.Judge_PersonID = FRJudge.judge2id
                              OR Judge.Judge_PersonID = FRJudge.judge3id
                            )
              FOR
                XML PATH('')
              ), 1, 1, '') AS BoutsJudged ,
        Persons.LastName + ' ' + Persons.FirstName AS JudgeName ,
        Events.EventName ,
        Events.EventDate
FROM    dbo.tblEventJudge Judge
        INNER JOIN dbo.tblPersons Persons ON PersonID = Judge_PersonID
        INNER JOIN dbo.tblEvents Events ON Events.EventID = Judge.EventID
WHERE   Judge.EventID = 1278;

The problem is that the STUFF command returns the following string:

1,10,11,12,13,14,15,16,17,18,19,2,3,4,5,6,7,8,9

How can I make it sort the numbers before concatenating it into a string?


回答1:


Try this

SELECT  ROW_NUMBER() OVER ( ORDER BY Judge.EventJudgeID ) AS JudgeRow ,
        Judge.EventID ,
        Judge.Judge_PersonID ,
        STUFF(Select ',' + CAST(Fights.FightNumber AS VARCHAR(MAX)) AS [text()] From ( SELECT DISTINCT Fights.FightNumber
                FROM    dbo.tblFights Fights ,
                        dbo.tblFightJudge FRJudge
                WHERE   Fights.FightID = FRJudge.fightid
                        AND ( Judge.Judge_PersonID = FRJudge.judge1id
                              OR Judge.Judge_PersonID = FRJudge.judge2id
                              OR Judge.Judge_PersonID = FRJudge.judge3id
                            )
                      ) X 
             ORDER BY Fights.FightNumber
             FOR
                XML PATH('')
              ), 1, 1, '') AS BoutsJudged ,
        Persons.LastName + ' ' + Persons.FirstName AS JudgeName ,
        Events.EventName ,
        Events.EventDate
FROM    dbo.tblEventJudge Judge
        INNER JOIN dbo.tblPersons Persons ON PersonID = Judge_PersonID
        INNER JOIN dbo.tblEvents Events ON Events.EventID = Judge.EventID
WHERE   Judge.EventID = 1278;

You can check below sqls,

Before :

Select *, 
       STUFF((Select Distinct ','+Cast(high as varchar(MAX)) 
              from master..spt_values where type = 'p' and number < 20  
              for xml Path('')),1,1,'')
from  master..spt_values where type = 'p' and number < 20

After :

Select *, 
           STUFF((Select ','+Cast(high as varchar(MAX)) from (Select distinct     high 
    from master..spt_values where type = 'p' and number < 20) x Order by high  for xml Path('')),1,1,'')
    from  master..spt_values where type = 'p' and number < 20



回答2:


I apologize for this solution being pedantic, but I have a hard time parsing code and need to see things in steps. Also, Microsoft adds a feature to do this in the 2012 release, but this code should work in most releases. First, use a database open to users in most SQLServers...

USE MASTER; SELECT TOP 3 TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS;
/*TABLE_NAME      COLUMN_NAME         ORDINAL_POSITION
spt_fallback_db   xserver_name        1
spt_fallback_db   xdttm_ins           2
spt_fallback_db   xdttm_last_ins_upd  3
*/

Now, breaking down this approach (to sorting a list within a column)...
(1) Adding FOR XML PATH('') to a 1 column query pivots it to one row, but adds XML tags for the column header...

SELECT TOP 3 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS FOR XML PATH('');
/*<COLUMN_NAME>xserver_name</COLUMN_NAME><COLUMN_NAME>xdttm_ins</COLUMN_NAME><COLUMN_NAME>xdttm_last_ins_upd</COLUMN_NAME>*/

(2) Concatination nullifies the column header, eliminating the tags. Any string will work, I want comma space...

SELECT TOP 3 ', ' + COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS FOR XML PATH('');
/*, xserver_name, xdttm_ins, xdttm_last_ins_upd*/

(3) Other columns will need their own SELECT, so FOR XML must be a subquery, and ORDER BY is a legal prefix in a FOR XML subquery ;)...

SELECT    TOP 2 TABLE_NAME
        , (SELECT    ', ' + COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
               WHERE COLUMNS.TABLE_NAME = TABLES.TABLE_NAME
            ORDER BY ORDINAL_POSITION FOR XML PATH('')
          ) LIST_OF_COLUMNS
     FROM INFORMATION_SCHEMA.TABLES
 ORDER BY TABLE_NAME;
/*TABLE_NAME      LIST_OF_COLUMNS
spt_fallback_db   , xserver_name, xdttm_ins, xdttm_last_ins_upd, xfallback_dbid, name, dbid, status, version
spt_fallback_dev  , xserver_name, xdttm_ins, xdttm_last_ins_upd, xfallback_low, xfallback_drive, low, high, status, name, phyname
*/

(4) Finally, SUBSTRING is more familiar to me than STUFF for removing a known prefix...

SELECT    TOP 2 TABLE_NAME
        , SUBSTRING((SELECT    ', ' + COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
                         WHERE COLUMNS.TABLE_NAME = TABLES.TABLE_NAME
                      ORDER BY ORDINAL_POSITION FOR XML PATH('')
                        )
                   , 2+1--Add 1 to start substring after the first 2 characters
                   , 99999) LIST_OF_COLUMNS
     FROM INFORMATION_SCHEMA.TABLES
 ORDER BY TABLE_NAME;
/*TABLE_NAME      LIST_OF_COLUMNS
spt_fallback_db    xserver_name, xdttm_ins, xdttm_last_ins_upd, xfallback_dbid, name, dbid, status, version
spt_fallback_dev   xserver_name, xdttm_ins, xdttm_last_ins_upd, xfallback_low, xfallback_drive, low, high, status, name, phyname
*/

Pedantically yours - Jim Gettman



来源:https://stackoverflow.com/questions/32077123/sort-data-before-concatenating-using-stuff-for-xml

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