Самый быстрый способ разбить строку, если известно максимальное количество столбцов, - это использовать метод Cascading CROSS APPLY . Допустим, вы знаете, что их будет не более 10 элементов в вашей строке. Вы могли бы сделать это:
DECLARE @string varchar(1000) = '[Key1:Value1:Value2:Value3:Value4:Value5]'
SELECT
[key] = SUBSTRING(t.string,1,d1.d-1),
col1 = SUBSTRING(t.string,d1.d+1,d2.d-d1.d-1),
col2 = SUBSTRING(t.string,d2.d+1,d3.d-d2.d-1),
col3 = SUBSTRING(t.string,d3.d+1,d4.d-d3.d-1),
col4 = SUBSTRING(t.string,d4.d+1,d5.d-d4.d-1),
col5 = SUBSTRING(t.string,d5.d+1,d6.d-d5.d-1),
col6 = SUBSTRING(t.string,d6.d+1,d7.d-d5.d-1),
col7 = SUBSTRING(t.string,d7.d+1,d8.d-d5.d-1),
col8 = SUBSTRING(t.string,d8.d+1,d9.d-d5.d-1),
col9 = SUBSTRING(t.string,d9.d+1,d10.d-d5.d-1)
FROM (VALUES (REPLACE(REPLACE(@string,']',':'),'[',''))) t(string)
CROSS APPLY (VALUES (CHARINDEX(':',t.string))) d1(d)
CROSS APPLY (VALUES (NULLIF(CHARINDEX(':',t.string,d1.d+1),0))) d2(d)
CROSS APPLY (VALUES (NULLIF(CHARINDEX(':',t.string,d2.d+1),0))) d3(d)
CROSS APPLY (VALUES (NULLIF(CHARINDEX(':',t.string,d3.d+1),0))) d4(d)
CROSS APPLY (VALUES (NULLIF(CHARINDEX(':',t.string,d4.d+1),0))) d5(d)
CROSS APPLY (VALUES (NULLIF(CHARINDEX(':',t.string,d5.d+1),0))) d6(d)
CROSS APPLY (VALUES (NULLIF(CHARINDEX(':',t.string,d6.d+1),0))) d7(d)
CROSS APPLY (VALUES (NULLIF(CHARINDEX(':',t.string,d7.d+1),0))) d8(d)
CROSS APPLY (VALUES (NULLIF(CHARINDEX(':',t.string,d8.d+1),0))) d9(d)
CROSS APPLY (VALUES (NULLIF(CHARINDEX(':',t.string,d9.d+1),0))) d10(d);
Использование этого метода для таблицы со строками, хранящимися в строках, будет выглядеть так:
DECLARE @table TABLE (someid int identity, somestring varchar(1000));
INSERT @table(somestring) VALUES
('[Key1:Value1:Value2:Value3:Value4:Value5]'),
('[Key2:Value1:Value2:Value3:Value4:Value5]'),
('[Key3:Value1:Value2:Value3:Value4:Value5]'),
('[Key4:Value1:Value2:Value3:Value4:Value5:Value6:Value7:Value8]'),
('[Key5:Value1:Value2:Value3:Value4:Value5:Value6:Value7:Value8:Value9:Value10]');
SELECT *
FROM @table s
CROSS APPLY
(
SELECT
[key] = SUBSTRING(t.string,1,d1.d-1),
dCount = LEN(t.string)-LEN(REPLACE(t.string,':','')),
col1 = SUBSTRING(t.string,d1.d+1,d2.d-d1.d-1),
col2 = SUBSTRING(t.string,d2.d+1,d3.d-d2.d-1),
col3 = SUBSTRING(t.string,d3.d+1,d4.d-d3.d-1),
col4 = SUBSTRING(t.string,d4.d+1,d5.d-d4.d-1),
col5 = SUBSTRING(t.string,d5.d+1,d6.d-d5.d-1),
col6 = SUBSTRING(t.string,d6.d+1,d7.d-d6.d-1),
col7 = SUBSTRING(t.string,d7.d+1,d8.d-d7.d-1),
col8 = SUBSTRING(t.string,d8.d+1,d9.d-d8.d-1),
col9 = SUBSTRING(t.string,d9.d+1,d10.d-d9.d-1)
FROM (VALUES (REPLACE(REPLACE(s.somestring,']',':'),'[',''))) t(string)
CROSS APPLY (VALUES (CHARINDEX(':',t.string))) d1(d)
CROSS APPLY (VALUES (NULLIF(CHARINDEX(':',t.string,d1.d+1),0))) d2(d)
CROSS APPLY (VALUES (NULLIF(CHARINDEX(':',t.string,d2.d+1),0))) d3(d)
CROSS APPLY (VALUES (NULLIF(CHARINDEX(':',t.string,d3.d+1),0))) d4(d)
CROSS APPLY (VALUES (NULLIF(CHARINDEX(':',t.string,d4.d+1),0))) d5(d)
CROSS APPLY (VALUES (NULLIF(CHARINDEX(':',t.string,d5.d+1),0))) d6(d)
CROSS APPLY (VALUES (NULLIF(CHARINDEX(':',t.string,d6.d+1),0))) d7(d)
CROSS APPLY (VALUES (NULLIF(CHARINDEX(':',t.string,d7.d+1),0))) d8(d)
CROSS APPLY (VALUES (NULLIF(CHARINDEX(':',t.string,d8.d+1),0))) d9(d)
CROSS APPLY (VALUES (NULLIF(CHARINDEX(':',t.string,d9.d+1),0))) d10(d)
) split
WHERE LEN(s.somestring)-LEN(REPLACE(s.somestring,':','')) < 10
Если вы не знаете максимального количества возможных элементов, вы можете взять эту логику и обернуть ее в какой-нибудь динамический SQL, который создает правильное количество CROSS APPLY. У меня нет времени, чтобы собрать эту логику, но, чтобы получить максимальное количество возможных разделителей, вы можете сделать что-то вроде этого:
DECLARE @maxDelimiters tinyint =
(SELECT MAX(LEN(s.somestring)-LEN(REPLACE(s.somestring,':',''))) FROM @table s);
В качестве альтернативы, если вы хотите использовать технику Джона, вы также можете использовать Dynamic SQL для создания его запроса с точным количеством требуемых значений «pos».