'Multi-character delimiter with STRING_SPLIT

I want to split values based on multiple values like ! and %, operators, but STRING_SPLIT only allows 1-character delimiters.

My string is like this:

99001316 - ABCD 250 ML!%!%99001234 - CDEF 500 ML!%!%99001995 - OWEW 50 ML

And what I want is to separate the numeric and text value. When I do this:

SELECT value FROM STRING_SPLIT(@x, '%',1)

I'm getting

value
99001234 - ABCD 250 ML!
!
99001230 - CDEF 500 ML!
!
99001995 - OWEW 50 ML

And what I want is

value string
99001234 ABCD 250 ML!
99001230 CDEF 500 ML!
99001995 OWEW 50 ML


Solution 1:[1]

One option is to parse the string via JSON. You may notice the delimiter of '!%!%'

Declare @X varchar(max) = '99001316 - ABCD 250 ML!%!%99001230 - CDEF 500 ML!%!%99001395 - OWEW 50 ML'


Select Value  = rtrim(left(value,charindex('-',Value+'-')-1))
      ,String = ltrim(substring(value,charindex('-',Value+'-')+1,len(Value)))
 From  OpenJSON( '["'+replace(string_escape(@X,'json'),'!%!%','","')+'"]' )

Resutls

Value       String
99001316    ABCD 250 ML
99001230    CDEF 500 ML
99001395    OWEW 50 ML

Solution 2:[2]

Another way to do it.

SQL

DECLARE @tokens NVARCHAR(max) = N'99001316 - ABCD 250 ML!%!%99001234 - CDEF 500 ML!%!%99001995 - OWEW 50 ML';

WITH rs AS
(
    SELECT value AS token 
    FROM STRING_SPLIT(REPLACE(@tokens,'!%!%','%') , '%')
)
SELECT * 
, LEFT(token, pos - 1) AS [value]
, RIGHT(token, LEN(token) - pos - 1) AS [string]
FROM rs
    CROSS APPLY (SELECT CHARINDEX('-', token)) AS t(pos);

Output

+------------------------+-----+-----------+-------------+
|         token          | pos |   value   |   string    |
+------------------------+-----+-----------+-------------+
| 99001316 - ABCD 250 ML |  10 | 99001316  | ABCD 250 ML |
| 99001234 - CDEF 500 ML |  10 | 99001234  | CDEF 500 ML |
| 99001995 - OWEW 50 ML  |  10 | 99001995  | OWEW 50 ML  |
+------------------------+-----+-----------+-------------+

Solution 3:[3]

Here are some more ideas

DECLARE @X VARCHAR(MAX) = '99001316 - ABCD 250 ML!%!%99001230 - CDEF 500 ML!%!%99001395 - OWEW 50 ML';

WITH s1 AS  (
            SELECT      ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS ord1
                        , s1.value
            FROM        STRING_SPLIT(REPLACE(@X, '!%!%', '|'), '|') s1
            ),
s2 AS       (
            SELECT      s1.ord1
                        , ROW_NUMBER() OVER (PARTITION BY s1.ord1 ORDER BY (SELECT 1)) AS ord2
                        , s2.value
            FROM        s1
            CROSS APPLY STRING_SPLIT(REPLACE(s1.value, ' - ', '|'), '|') s2
            )
SELECT      MAX(CASE WHEN s2.ord2 = 1 THEN s2.value END) AS id
            , MAX(CASE WHEN s2.ord2 = 2 THEN s2.value END) AS text
FROM        s2
GROUP BY    s2.ord1

You can include ordinal column with STRING_SPLIT on Azure (passing 1 in 3-rd parameter) and remove the ROW_NUMBER usage for both ord columns above.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 miriamka
Solution 3 wqw