Try using a number/tally table:INSERT INTO #OptionListSELECT DISTINCT D1.OptionId, D2.OptionDescFROM( SELECT UserRole ,SUBSTRING(R.OptionIdString ,N.Number + 1 ,CHARINDEX('&', R.OptionIdString, N.Number + 1) - N.Number - 1) as OptionId ,ROW_NUMBER() OVER (PARTITION BY R.UserRole ORDER BY N.Number) AS FieldNo FROM #DenormalizedRole R JOIN master.dbo.spt_values N ON N.[Type] = 'P' AND N.Number < LEN(R.OptionIdString) AND SUBSTRING(R.OptionIdString, N.Number, 1) = '&') D1 JOIN ( SELECT UserRole ,SUBSTRING(R.OptionDescString ,N.Number + 1 ,CHARINDEX('&', R.OptionDescString, N.Number + 1) - N.Number - 1) as OptionDesc ,ROW_NUMBER() OVER (PARTITION BY R.UserRole ORDER BY N.Number) AS FieldNo FROM #DenormalizedRole R JOIN master.dbo.spt_values N ON N.[Type] = 'P' AND N.Number < LEN(R.OptionDescString) AND SUBSTRING(R.OptionDescString, N.Number, 1) = '&' ) D2 ON D1.UserRole = D2.UserRole AND D1.FieldNo = D2.FieldNoINSERT INTO #UserRole_XREF_OptionIdSELECT UserRole ,SUBSTRING(R.OptionIdString ,N.Number + 1 ,CHARINDEX('&', R.OptionIdString, N.Number + 1) - N.Number - 1) as OptionIdFROM #DenormalizedRole R JOIN master.dbo.spt_values N ON N.[Type] = 'P' AND N.Number < LEN(R.OptionIdString) AND SUBSTRING(R.OptionIdString, N.Number, 1) = '&'