1

How can i get the result set as TESTING1,TESTING2 from below

DECLARE @MyString varchar(256) = '$I10~TESTING1$XYZ$I10~TESTING2$~'

Basically i need to get all the substrings which are in between $I10~ and $

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
shivareddy
  • 15
  • 1
  • 6

1 Answers1

2

See inline comments for an overview of what's going on:

DECLARE @MyString varchar(256) = '$I10~TESTING1$XYZ$I10~TESTING2$~'
, @pre char(5) = '$I10~' --this appears before the string we want
, @post char(1) = '$' --this appears after it
select 
--take part of
substring( 
    --the input string
    @MyString     
    --starting from the first pre-delimiter (but add on the length of the delimeter so we exclude the delimeter itself)
    ,charindex(@pre,@MyString) + len(@pre) 
    --and ending at the first post-delimiter to appear after the first pre-delimeter
    , charindex(@post,@MyString,charindex(@pre,@MyString) + len(@pre)) - (charindex(@pre,@MyString) + len(@pre))
) 
,
--for the second result do the same as above
substring(
    @MyString
    --only now we're looking for the second pre-delimiter (aka the first after the first)
    ,charindex(@pre,@MyString,charindex(@pre,@MyString) + len(@pre)) + len(@pre)
    --and the second post-delimiter
    ,charindex(@post,@MyString,charindex(@pre,@MyString,charindex(@pre,@MyString) + len(@pre)) + len(@pre)) - (charindex(@pre,@MyString,charindex(@pre,@MyString) + len(@pre)) + len(@pre))
) 

NB: this assumes that the pre delimiter doesn't appear between the pre and post delimiter; if it did that could get confusing / we'd need to determine the desired behaviour.

substring(@stringToBreakApart, @indexOfFirstCharacterInSubstring, @lengthOfResultingString) - returns a segment of the original string.

charindex(@stringToFind, @stringToSearch, @indexOfFirstCharacterToLookAt) - returns the index of the first character of a given substring within a given string.

len(@stringToAnalyze) - returns the number of characters (length) of the given string.

Update

Per comments, here's how you could return a single column which splits the string on the delimiters (ignoring anything not between pre and post) and then joins the results to form a comma delimited field.

DECLARE @MyString varchar(256) = '$I10~TESTING1$XYZ$I10~TESTING2$~$I10~TESTING3$...'
, @pre char(5) = '$I10~' --this appears before the string we want
, @post char(1) = '$' --this appears after it
, @newDelim char(1) = ','
;with cte(indx, firstCharIndex, lastCharIndex) as 
(
    select 0 
    , charindex(@pre,@MyString) + len(@pre) 
    , charindex(@post,@MyString,charindex(@pre,@MyString) + len(@pre)) 

    union all

    select indx + 1
    , charindex(@pre,@MyString, lastCharIndex + len(@post)) + len(@pre) 
    , charindex(@post,@MyString,charindex(@pre,@MyString, lastCharIndex + len(@post)) + len(@pre)) 
    from cte
    where charindex(@pre,@MyString, lastCharIndex + len(@post)) > 0
)
, cte2 (substr, indx ) as 
(
    select cast(substring(@MyString, firstCharIndex, lastCharIndex - firstCharIndex) as nvarchar(max)) 
    , indx
    from cte 
    where indx = (select max(indx) from cte)

    union all

    select substring(@MyString, firstCharIndex, lastCharIndex - firstCharIndex) + @newDelim + cte2.substr
    , cte.indx
    from cte
    inner join cte2 on cte2.indx = cte.indx + 1 

)
select * from cte2 where indx = 0
JohnLBevan
  • 22,735
  • 13
  • 96
  • 178
  • Thank you....this will work, but if we have 10 values like (TESTING1,TESTING2...TESTING10) exists between the delimiters '$I10~' and '$', we have to use the substring 10 times. Is there a way we can do it dynamic – shivareddy Oct 21 '15 at 22:33
  • Yes... see http://stackoverflow.com/questions/2647/how-do-i-split-a-string-so-i-can-access-item-x. However if you have multiple results you'll have an issue; SQL makes it easy to return a dynamic number of rows, but harder to return a dynamic number of columns... so consider how you want your output to work (i.e. do you want multiple rows for each source row, do you want to restrict the number of columns to some upper limit (with nulls filling the fields for which all columns weren't required), or do you want to join your results again after splitting, only with a simpler delimiter.. – JohnLBevan Oct 21 '15 at 22:39
  • I want the result in one column as TESTING1,TESTING2,TESTING3...TESTINGN... I am using the function from below path https://www.berezniker.com/content/pages/sql/microsoft-sql-server/strextract-udf-retrieves-string-between-two-delimiters but this will handle only single occurence in the string... can we tweak this function to handle multiple occurences – shivareddy Oct 21 '15 at 22:46
  • FYI: I've updated the solution per your comments... sadly don't have time to comment this evening, but basically it uses the same concept as my original answer to split the string and return a row per result (cte), then joins these results up again (cte2), then selects the row in which all results have been joined together. – JohnLBevan Oct 21 '15 at 23:11
  • 1
    @JohnLBevan The updated answer gives the first occurrence of the pattern, say 123|abc adf *456|asdfaf is the input string and start char is * and end char is |, the query returns 123 and not 123 and 456 – Ram Venkat Jun 28 '20 at 17:35
  • 1
    Thanks @RamVenkat; good spot... I'll come back and revise this sometime when I have time. – JohnLBevan Jun 29 '20 at 07:16