SQL拆分函数和排序问题?

时间:2013-10-06 14:10:18

标签: sql tsql sql-server-2008-r2

我有以下Split功能,

ALTER FUNCTION [dbo].[Split](@String varchar(8000), @Delimiter char(1))     
                returns @temptable TABLE (items varchar(8000))     
            as     
            begin
                set @String = RTRIM(LTRIM(@String))
                declare @idx int     
                declare @slice varchar(8000)     

                select @idx = 1     
                    if len(@String)<1 or @String is null  return     

                while @idx!= 0     
                begin     
                    set @idx = charindex(@Delimiter,@String)     
                    if @idx!=0     
                        set @slice = left(@String,@idx - 1)     
                    else     
                        set @slice = @String     

                    if(len(@slice)>0)
                        insert into @temptable(Items) values(@slice)     

                    set @String = right(@String,len(@String) - @idx)     
                    if len(@String) = 0 break     
                end 
            return     
            end

当我写作时,

SELECT Items 
FROM Split('around the home,clean and protect,soaps and air fresheners,air fresheners',',')

这会给我,

air fresheners
around the home
clean and protect
soaps and air fresheners

我需要维持秩序。

5 个答案:

答案 0 :(得分:4)

一个更简单的功能:

CREATE FUNCTION dbo.SplitStrings_Ordered
(
    @List       nvarchar(MAX),
    @Delimiter  nvarchar(255)
)
RETURNS TABLE
AS
RETURN 
(
  SELECT [Index] = CHARINDEX(@Delimiter, @List + @Delimiter, Number),
         Item = SUBSTRING(@List, Number, CHARINDEX(@Delimiter, 
                @List + @Delimiter, Number) - Number)
    FROM 
    (
      SELECT ROW_NUMBER() OVER (ORDER BY [object_id]) FROM sys.all_objects
    ) AS n(Number)
    WHERE Number <= CONVERT(INT, LEN(@List))
    AND SUBSTRING(@Delimiter + @List, Number, LEN(@Delimiter)) = @Delimiter
);
GO

样本用法:

DECLARE @s nvarchar(MAX) = N',around the home,clean and protect,soaps and air'
  + ' fresheners,air fresheners';

SELECT Item FROM dbo.SplitStrings_Ordered(@s, N',') ORDER BY [Index];

或者从输入订购的表中返回订单:

SELECT o.OrderID
  FROM dbo.Orders AS o
  INNER JOIN dbo.SplitStrings_Ordered('123,789,456') AS f
  ON o.OrderID = CONVERT(int, f.Item)
  ORDER BY f.[Index];

答案 1 :(得分:1)

您的函数需要设置一个订单列(此示例中为seq):

ALTER FUNCTION [dbo].[Split](@String varchar(8000), @Delimiter char(1))     
            returns @temptable TABLE (seq int, items varchar(8000))     
        as     
        begin
            set @String = RTRIM(LTRIM(@String))
            declare @idx int     
            declare @seq int
            declare @slice varchar(8000)     

            set @seq=1

            select @idx = 1     
                if len(@String)<1 or @String is null  return     

            while @idx!= 0     
            begin     
                set @idx = charindex(@Delimiter,@String)     
                if @idx!=0     
                    set @slice = left(@String,@idx - 1)     
                else     
                    set @slice = @String     

                if(len(@slice)>0)
                begin
                    set @seq = @seq + 1
                    insert into @temptable(seq, Items) values(@seq,@slice)     
                end

                set @String = right(@String,len(@String) - @idx)     
                if len(@String) = 0 break     
            end 
        return     
        end
GO
SELECT * FROM Split('around the home,clean and protect,soaps and air fresheners,air fresheners',',') order by seq 

答案 2 :(得分:1)

declare @Version nvarchar(3000)
declare @Delimiter char(1) = ','
declare @result nvarchar(3000)
set @Version = 'Terça-feira, Quarta-feira, Sexta-feira, Segunda-feira';

with V as (select value v, Row_Number() over (order by (select 0)) n 
    from String_Split(@Version, @Delimiter)
)
    SELECT @result = STUFF((SELECT ', ' + RTRIM(LTRIM(v))
      FROM V
      ORDER BY CASE RTRIM(LTRIM(v))
            WHEN 'Segunda-feira' then 1 
            WHEN 'Terça-feira' then 2
            WHEN 'Quarta-feira' then 3 
            WHEN 'Quinta-feira' then 4 
            WHEN 'Sexta-feira' then 5
           END FOR XML PATH('')), 1, LEN(@Delimiter), '')
PRINT @result

答案 3 :(得分:0)

如果您可以遵守SQL Server的兼容级别130,则可以使用String_Split()函数。

使用此函数和Row_Number()函数,您可以返回包含原始序列的表。例如:

declare @Version nvarchar(128)
set @Version = '1.2.3';

with V as (select value v, Row_Number() over (order by (select 0)) n 
    from String_Split(@Version, '.')
)
    select
        (select v from V where n = 1) Major,
        (select v from V where n = 2) Minor,
        (select v from V where n = 3) Revision

请注意,Row_Number需要排序,但是如果您传递文字值,则结果将位于解析的序列中。不能保证将来的SQL Server版本会发生这种情况,因为根据String_Split文档,没有正式订购。我怀疑微软是否会破坏这一点,至少在引入该功能的版本之前应按顺序返回该命令,但是与此同时,在编写决定是否发射导弹的代码时,最好不要依赖此命令。 / p>

返回:

Major Minor Revision
----- ----- --------
1     2     3

答案 4 :(得分:0)

当您的字符串具有1000个或更多的值要拆分时,这将是一个更快的解决方案。对于表值函数,要进行任何排序,必须在使用位置应用“ ORDER BY”。这是因为按照惯例,没有“ ORDER BY”的表中的“ SELECT”没有任何排序。

CREATE FUNCTION [dbo].[Split]
( 
    @String VARCHAR(max), 
    @Delimiter VARCHAR(max)
) 
RETURNS @Data TABLE 
(
    [Order] INT IDENTITY(1,1), 
    [Value] VARCHAR(max)
)
AS
BEGIN 
    DECLARE @x XML = cast('<i>' + replace(@String, @Delimiter, '</i><i>') + '</i>' AS XML)
    INSERT INTO @Data 
    SELECT v.value('.', 'varchar(max)') FROM @x.nodes('i') AS x(v)
    RETURN 
END
GO