在SQL中重新排列字符串

时间:2015-05-26 16:40:43

标签: sql sql-server string sorting

所以我的数据库中有一个包含如下数据的引用字段:

 R8, R9, R1, R2, R3, R4, R5, R6, R7

我想重新安排这个字段看起来像这样:

R1, R2, R3, R4, R5, R6, R7, R8, R9

此外,还有一些其他类型的描述,例如:

C7 (OP1 to OP12), C10 (OP1 to OP12), C3 (IP1 to IP16), C6 (OP1 to OP12), C10, C11, C12, C45, C49, C50, C14 (OP1 to OP12), C5 (OP1 to OP12), C9 (OP1 to OP12), C15 (OP1 to OP12), C51, C52, C54, C55

哪个应重新排列,如下所示:

C3 (IP1 to IP16), C5 (OP1 to OP12), C6 (OP1 to OP12), C7 (OP1 to OP12), C9 (OP1 to OP12), C10, C11, C12, C14 (OP1 to OP12), and so on...

有没有办法用SQL命令对它进行排序?

4 个答案:

答案 0 :(得分:2)

以下是如何执行此操作的方法:

with cte as(select F1.id,
                   F1.data,
                   O.splitdata 
            from
            (select *,
                    cast('<X>'+replace(replace(F.data, '&', '&amp;'),', ','</X><X>')+'</X>' as XML) as xmlfilter 
             from t F)F1
            cross apply
            (select fdata.D.value('.','varchar(50)') as splitdata 
             from f1.xmlfilter.nodes('X') as fdata(D)) O)

select id,
       (select stuff((select ',' + rtrim(splitdata)
        from cte 
        where id = c.id 
        order by cast(substring(splitdata, patindex('%[0-9]%', splitdata), case when charindex(' ', splitdata) > 0 then charindex(' ', splitdata) - patindex('%[0-9]%', splitdata) else len(splitdata) end) as int)
        for xml path('')),1,1,'') AS d) dt
from cte c
group by id

小提琴http://sqlfiddle.com/#!3/2c9b1/1

答案 1 :(得分:1)

这不是我创建的功能..但我认为这会对你有所帮助。这是由Andreas Goldman创建的。

首先运行此查询以创建名为F_ConvertStringToTable

的函数
CREATE FUNCTION F_ConvertStringToTable
(
@List VARCHAR(MAX), -- Separerad lista av värden
@Delimiter CHAR(1) -- Avgränsare/separator
) 
RETURNS @T TABLE (Col VARCHAR(MAX) NOT NULL) AS
    BEGIN
    WITH SEPARATEDTABLE (STARTVAL, STOPVAL) 
    AS 
    (
    SELECT 
    STARTVAL = CAST(1 AS BIGINT),
    STOPVAL = CHARINDEX(@Delimiter, @List + @Delimiter)

    UNION ALL

    SELECT 
    STARTVAL = STOPVAL + 1,
    STOPVAL = charindex(@Delimiter, @List + @Delimiter, STOPVAL + 1)
    FROM SEPARATEDTABLE
    WHERE STOPVAL > 0
    )
    INSERT @t(Col)
    SELECT LTRIM(RTRIM(SUBSTRING(@List, STARTVAL, CASE WHEN STOPVAL > 0 THEN STOPVAL - STARTVAL ELSE 0 END)))
    FROM SEPARATEDTABLE
    WHERE STOPVAL > 0

    OPTION (MAXRECURSION 0)

    RETURN
    END

然后你可以运行这样的功能。

select * from F_ConvertStringToTable('R8, R9, R1, R2, R3, R4, R5, R6, R7',',') order by col

第一个参数是列,第二个是分隔符。这基本上会将列值吐出到表中,然后您可以使用基本的order by子句对列进行排序。

修改

正如@Sean Lange所提到的,有一种更有效的方法。该函数实际上链接到与上面相同的线程,并以类似的方式使用。归功于Wayne。

运行以下查询以创建功能。此函数再次根据任何分隔符拆分行。

CREATE FUNCTION dbo.DelimitedSplit
        (
        @pString    VARCHAR(7999),
        @pDelimiter CHAR(1)
        )
RETURNS TABLE
   WITH SCHEMABINDING
AS
RETURN
WITH
      E1(N) AS ( --=== Create Ten 1's
                 SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 --10
               ),
      E2(N) AS (SELECT 1 FROM E1 a, E1 b),   --100
      E4(N) AS (SELECT 1 FROM E2 a, E2 b),   --10,000
cteTally(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT N)) FROM E4)  
--===== Do the split
 SELECT ROW_NUMBER() OVER (ORDER BY N) AS ItemNumber,
        SUBSTRING(@pString, N, CHARINDEX(@pDelimiter, @pString + @pDelimiter, N) - N) AS Item
   FROM cteTally
  WHERE N < LEN(@pString) + 2
    AND SUBSTRING(@pDelimiter + @pString, N, 1) = @pDelimiter
;
GO;

然后运行这样的函数。

select item from dbo.delimitedSplit('R8, R9, R1, R2, R3, R4, R5, R6, R7',',') order by item

This is where I got the answer

答案 2 :(得分:0)

正如评论中的其他人所提到的,永远不会在一列中存储多个值。考虑更改表结构以存储数据,这完全是非规范化形式。

如果你想获得结果,那么请使用它。

首先,您需要comp_*

Split String function

现在您可以使用函数结果来对结果集进行排序。

create function [dbo].[udf_splitstring] (@tokens    varchar(max),
                                         @delimiter varchar(5))
returns @split table (
  token varchar(20) not null )
as
  begin
      declare @list xml
      select @list = cast('<a>'+ replace(@tokens, @delimiter, '</a><a>') + '</a>' as xml)
      insert into @split(token)
      select ltrim(t.value('.', 'varchar(20)')) as data
      from   @list.nodes('/a') as x(t)
      return
  end
  go

答案 3 :(得分:0)

我假设你的名单不仅仅是列表。试试这个:

DECLARE @yourTable TABLE (ID INT,ref VARCHAR(100));

INSERT INTO @yourTable
VALUES  (1,'R8, R9, R1, R2, R3, R4, R5, R6, R7'),
        (2,'A4, A7, A1, A9, A2');

WITH CTE_XML
AS
(
    SELECT  ID,CAST('<t>' + REPLACE(ref,', ','</t><t>') + '</t>' AS XML) ref
    FROM @yourTable
)

SELECT ID,STUFF(sorted_ref,1,1,'') sorted_ref
FROM @yourTable A
CROSS APPLY(
            SELECT  ',' + xml_ref.value('.','VARCHAR(100)')
            FROM CTE_XML B
            CROSS APPLY ref.nodes('t') CA(xml_ref)
            WHERE A.ID = B.ID
            ORDER BY 1
            FOR XML PATH('')
            ) CA(sorted_ref)

结果:

ID          sorted_ref
----------- ------------------------------
1           R1,R2,R3,R4,R5,R6,R7,R8,R9
2           A1,A2,A4,A7,A9