A loop obviously comes to mind but we can do much better. This is where a tally or numbers table is perfect. I have a view in my system like this which creates such a table with 10,000 rows on demand. There are plenty of ways to create such a table, or you could create a persistent table for a slight performance gain.
create View [dbo].[cteTally] as
WITH
E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select N from cteTally
Now that we have the view this type of thing is super painless using a totally set based approach and abandoning the idea of looping.
declare @Something varchar(20) = 'ABCDEF'
select SUBSTRING(@Something, N, 1)
from cteTally t
where t.N < LEN(@Something)