If this were SQL Server 2022 (or later) we could:
- Use
STRING_SPLIT(,,1)
to break up the string at the \
delimiters and assign each part an ordinal
value.
- Filter out the even substrings (leaving the odd).
- Reassemble the remaining parts using
STRING_AGG()
.
- Add a
TRIM()
to clean up leading or trailing whitespace.
Something like:
SELECT
T.Code,
TRIM(STRING_AGG(SS.value, '') WITHIN GROUP(ORDER BY SS.ordinal)) AS Answer
FROM #t T
CROSS APPLY STRING_SPLIT(T.S, '\', 1) SS
WHERE SS.ordinal % 2 != 0 -- Keep the odd parts
GROUP BY T.Code
The third parameter of STRING_SPLIT()
generates an ordinal
along side each value
(available in SQL Server 2022 and later). This is referenced in the WITHIN GROUP(...)
clause of the STRING_AGG()
function to guarantee the proper ordering or the reassembled components.
If this is part of a more complicated query, you can encapsulate the logic within a CROSS APPLY
whose result can then be referenced elsewhere.
SELECT
T.Code,
CA.Answer
FROM #t T
CROSS APPLY (
SELECT TRIM(STRING_AGG(SS.value, '') WITHIN GROUP(ORDER BY SS.ordinal)) AS Answer
FROM STRING_SPLIT(T.S, '\', 1) SS
WHERE SS.ordinal % 2 != 0 -- Keep the odd parts
) CA
ORDER BY T.Code
For SQL Server 2019, the STRING_SPLIT()
function does not support the enable_ordinal
option, so this function cannot be used when we need to guarantee order. An alternative is to use a technique that first maps the string into a JSON array, and then uses OPENJSON()
to parse that array.
SELECT
T.Code,
CA.Answer
FROM #t T
CROSS APPLY (
SELECT TRIM(STRING_AGG(SS.value, '') WITHIN GROUP(ORDER BY SS.ordinal)) AS Answer
FROM (
SELECT J.value, CONVERT(int, [key]) + 1 AS ordinal
FROM OPENJSON(CONCAT('["', REPLACE(STRING_ESCAPE(T.S, 'json'), '\\', '","'), '"]')) J
) SS
WHERE SS.ordinal % 2 != 0 -- Keep the odd parts
) CA
ORDER BY T.Code
The above will convert the source string into a JSON array that is then parsed into individual elements by OPENJSON()
. The STRING_ESCAPE()
function protects the JSON against certain special characters (particularly any embedded double quotes) that would otherwise cause an error. The key
value returned by OPENJSON()
above is a zero-based string that must be converted to an integer and offset by 1 to match the STRING_SPLIT()
ordinal
.
NOTE: There are several STRING_SPLIT()
alternatives out there, but few yield a reliable ordinal
value. (There are actually several bad answers out there that use post-split numbering logic that appears to work, but whose results are not guaranteed by any documented feature, and which may break at any time - especially when scaled up.)
The results are the same for each of the above:
Code |
Answer |
1 |
String |
2 |
Laser |
3 |
Las and trings |
See this db<>fiddle for a demo.
An update in place can be performed with the following:
UPDATE T
SET S = CA.Answer
FROM #t T
CROSS APPLY (
SELECT TRIM(STRING_AGG(SS.value, '') WITHIN GROUP(ORDER BY SS.ordinal)) AS Answer
FROM (
SELECT J.value, CONVERT(int, [key]) + 1 AS ordinal
FROM OPENJSON(CONCAT('["', REPLACE(STRING_ESCAPE(T.S, 'json'), '\\', '","'), '"]')) J
) SS
WHERE SS.ordinal % 2 != 0 -- Keep the odd parts
) CA
Or if you prefer to include the calculation directly in the select statement:
UPDATE T
SET S = (
SELECT TRIM(STRING_AGG(SS.value, '') WITHIN GROUP(ORDER BY SS.ordinal))
FROM (
SELECT J.value, CONVERT(int, [key]) + 1 AS ordinal
FROM OPENJSON(CONCAT('["', REPLACE(STRING_ESCAPE(T.S, 'json'), '\\', '","'), '"]')) J
) SS
WHERE SS.ordinal % 2 != 0 -- Keep the odd parts
)
FROM #t T
See this updated db<>fiddle.