2017-07-03 231 views
2

我有這個功能(來源:searchsqlserver):如何通過索引從分隔字符串獲取元素?

CREATE FUNCTION dbo.fnSplit(
    @sInputList VARCHAR(8000)    -- List of delimited items 
    , @sDelimiter VARCHAR(8000) = ','  -- delimiter that separates items 
) RETURNS @List TABLE (item VARCHAR(8000)) 
    BEGIN 
    DECLARE @sItem VARCHAR(8000) 
    WHILE CHARINDEX(@sDelimiter,@sInputList,0) <> 0 
     BEGIN 
     SELECT 
     @sItem=RTRIM(LTRIM(SUBSTRING(@sInputList,1,CHARINDEX(@sDelimiter, 
      @sInputList,0)-1))), 
     @sInputList=RTRIM(LTRIM(SUBSTRING(@sInputList,CHARINDEX(@sDelimiter, 
      @sInputList,0)+LEN(@sDelimiter),LEN(@sInputList)))) 
     IF LEN(@sItem) > 0 
      INSERT INTO @List SELECT @sItem 
     END 

    IF LEN(@sInputList)> 0 
    INSERT INTO @List SELECT @sInputList -- Put the last item in 
    RETURN 
    END 
GO 

它作爲參數字符串和分隔符,並返回一個帶分隔符的元素之一。

select * from fnSplit('1,22,333', ',') -- returns 1 22 333 

我會承認,我對SQL很陌生,我根本無法遵循這個函數背後的整個邏輯。我試圖實現的是一個函數,它具有第三個參數(一個索引),並返回索引提及的位置上的元素。例如:

select * from fnSplit('1 22 333 444 5555 666', ' ' , 2) -- 333 

select * from fnSplit('1 22 333 444 5555 666', ' ' , 0) -- 1 
+0

如果您不熟悉SQL,請先了解如何正確存儲值。分隔字符串不是存儲值列表的SQLish方式。 –

+1

'我根本無法遵循這個函數背後的整個邏輯'使用多態表值函數來解析字符串不是很有效。無論如何http://rextester.com/RDU95829 – lad2025

+0

要存儲值的列表,我會使用表/ #temp table/@table變量('DECLARE @array TABLE(IntValue INT); INSERT @array(IntValue)VALUES(10 ),(20);')或XML(例如:'DECLARE @x XML =' '') –

回答

2

使用與multistatment表值UDF循環來解析字符串是非常低效的。 更好的方法:Split strings the right way – or the next best way


無論如何,如果你要調整你的功能,你可以設置表變量IDENTITY列,然後篩選基於第三個參數:

CREATE FUNCTION dbo.fnSplit(
    @sInputList VARCHAR(8000)    -- List of delimited items 
    , @sDelimiter VARCHAR(8000) = ','  -- delimiter that separates items 
    ,@num INT 
) RETURNS @List TABLE (item VARCHAR(8000)) 
    BEGIN 
    DECLARE @ListHelper AS TABLE(id INT IDENTITY(1,1), item VARCHAR(8000)); 
    DECLARE @sItem VARCHAR(8000) 
    WHILE CHARINDEX(@sDelimiter,@sInputList,0) <> 0 
     BEGIN 
     SELECT 
     @sItem=RTRIM(LTRIM(SUBSTRING(@sInputList,1,CHARINDEX(@sDelimiter, 
      @sInputList,0)-1))), 
     @sInputList=RTRIM(LTRIM(SUBSTRING(@sInputList,CHARINDEX(@sDelimiter, 
      @sInputList,0)+LEN(@sDelimiter),LEN(@sInputList)))) 
     IF LEN(@sItem) > 0 
      INSERT INTO @ListHelper SELECT @sItem 
     END 

    IF LEN(@sInputList)> 0 
    INSERT INTO @ListHelper SELECT @sInputList -- Put the last item in 
    INSERT INTO @List 
    SELECT item 
    FROM @ListHelper 
    WHERE id = @num 
    RETURN 
    END 
GO 

select * from fnSplit('1 22 333 444 5555 666', ' ' , 3); 
--333 

LiveDemo

1

首先,循環拆分效率不高。

與索引篩選實施例

Select * 
From [dbo].[udf-Str-Parse-8K]('1 22 333 444 5555 666', ' ' ) 
Where RetSeq=3 

返回

RetSeq RetVal 
3  333 

實施例不具有索引篩選

Select * 
From [dbo].[udf-Str-Parse-8K]('1 22 333 444 5555 666', ' ' ) 

返回

RetSeq RetVal 
1  1 
2  22 
3  333 
4  444 
5  5555 
6  666 

的UDF使用

CREATE FUNCTION [dbo].[udf-Str-Parse-8K] (@String varchar(max),@Delimiter varchar(25)) 
Returns Table 
As 
Return ( 
    with cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)), 
      cte2(N) As (Select Top (IsNull(DataLength(@String),0)) Row_Number() over (Order By (Select NULL)) From (Select N=1 From cte1 a,cte1 b,cte1 c,cte1 d) A), 
      cte3(N) As (Select 1 Union All Select t.N+DataLength(@Delimiter) From cte2 t Where Substring(@String,t.N,DataLength(@Delimiter)) = @Delimiter), 
      cte4(N,L) As (Select S.N,IsNull(NullIf(CharIndex(@Delimiter,@String,s.N),0)-S.N,8000) From cte3 S) 

    Select RetSeq = Row_Number() over (Order By A.N) 
      ,RetVal = LTrim(RTrim(Substring(@String, A.N, A.L))) 
    From cte4 A 
); 
--Orginal Source http://www.sqlservercentral.com/articles/Tally+Table/72993/ 
--Select * from [dbo].[udf-Str-Parse-8K]('Dog,Cat,House,Car',',') 
--Select * from [dbo].[udf-Str-Parse-8K]('John||Cappelletti||was||here','||') 
1

你可以改變你分割字符串函數,包括行號:

CREATE FUNCTION dbo.fnSplit2(
    @sInputList VARCHAR(8000)    -- List of delimited items 
    , @sDelimiter VARCHAR(8000) = ','  -- delimiter that separates items 
) RETURNS @List TABLE (item VARCHAR(8000)) 
    BEGIN 
    DECLARE @sItem VARCHAR(8000), 
      @RowNumber int 

    set @RowNumber = 0 

    WHILE CHARINDEX(@sDelimiter,@sInputList,0) <> 0 
     BEGIN 


      SELECT 
      @sItem=RTRIM(LTRIM(SUBSTRING(@sInputList,1,CHARINDEX(@sDelimiter, 
       @sInputList,0)-1))), 
      @sInputList=RTRIM(LTRIM(SUBSTRING(@sInputList,CHARINDEX(@sDelimiter, 
       @sInputList,0)+LEN(@sDelimiter),LEN(@sInputList)))) 
      IF LEN(@sItem) > 0 
       INSERT INTO @List SELECT @sItem, @RowNumber 

      set @RowNumber = @RowNumber + 1 
     END 

    set @RowNumber = @RowNumber + 1 

    IF LEN(@sInputList)> 0 
    INSERT INTO @List SELECT @sInputList, @RowNumber -- Put the last item in 
    RETURN 
    END 
相關問題