For large strings, I found the UDF loop approach much more efficient than the xml approach.
create Function [Split_Text_Into_Table_Multi_Column] ( @Row_Delimit_Char Varchar(5) ,@Column_Delimit_Char Varchar(5) ,@Text_To_Split Varchar(Max) ) Returns @Output_Table Table ( row_id Int Identity (1, 1) ,column_1 Varchar(Max) Collate Latin1_General_CS_AS ,column_2 Varchar(Max) Collate Latin1_General_CS_AS ,column_3 Varchar(Max) Collate Latin1_General_CS_AS ) As Begin Declare @Column_Delimit_Index_1 Int ,@Column_Delimit_Index_2 Int ,@String Varchar(Max) ,@String_1 Varchar(Max) ,@String_2 Varchar(Max) ,@String_3 Varchar(Max) ,@String_Len Int ,@Delimit_Len TinyInt Declare @Last_Delimit_Index Int = 0 ,@Next_Delimit_Index Int = 1 --always treat new line as delimiter Select @Text_To_Split = Replace(Replace(@Text_To_Split, Char(10), @Row_Delimit_Char), Char(13), @Row_Delimit_Char) --begin loop while next delimiter found While @Next_Delimit_Index > 0 Begin --find next delimiter Select @Next_Delimit_Index = CharIndex(@Row_Delimit_Char, @Text_To_Split, @Last_Delimit_Index + 1) --get value to insert Select @String = Ltrim(Rtrim(Substring(@Text_To_Split, @Last_Delimit_Index + 1, Case When @Next_Delimit_Index = 0 Then Len(@Text_To_Split) - @Last_Delimit_Index Else @Next_Delimit_Index - @Last_Delimit_Index - 1 End))) --Insert Non blank strings only If @String <> '' Begin Select @Delimit_Len = Len(@Column_Delimit_Char) Select @String_Len = DataLength(@String)/*includes trailing spaces*/ ,@Column_Delimit_Index_1 = 1 ,@Column_Delimit_Index_2 = IsNull(NullIf(CharIndex(@Column_Delimit_Char, @String, @Column_Delimit_Index_1), 0), @String_Len + 1) ,@String_1 = Case When @Column_Delimit_Index_2 <= @Column_Delimit_Index_1 Or @Column_Delimit_Index_2 - @Column_Delimit_Index_1 = 0 Then Null Else Substring(@String, @Column_Delimit_Index_1, @Column_Delimit_Index_2 - @Column_Delimit_Index_1) End ,@Column_Delimit_Index_1 = @Column_Delimit_Index_2 + @Delimit_Len ,@Column_Delimit_Index_2 = IsNull(NullIf(CharIndex(@Column_Delimit_Char, @String, @Column_Delimit_Index_1), 0), @String_Len + 1) ,@String_2 = Case When @Column_Delimit_Index_2 <= @Column_Delimit_Index_1 Or @Column_Delimit_Index_2 - @Column_Delimit_Index_1 = 0 Then Null Else Substring(@String, @Column_Delimit_Index_1, @Column_Delimit_Index_2 - @Column_Delimit_Index_1) End ,@Column_Delimit_Index_1 = @Column_Delimit_Index_2 + @Delimit_Len ,@Column_Delimit_Index_2 = IsNull(NullIf(CharIndex(@Column_Delimit_Char, @String, @Column_Delimit_Index_1), 0), @String_Len + 1) ,@String_3 = Case When @Column_Delimit_Index_2 <= @Column_Delimit_Index_1 Or @Column_Delimit_Index_2 - @Column_Delimit_Index_1 = 0 Then Null Else Substring(@String, @Column_Delimit_Index_1, @Column_Delimit_Index_2 - @Column_Delimit_Index_1) End Insert Into @Output_Table ( column_1 ,column_2 ,column_3) Values ( @String_1 ,@String_2 ,@String_3) End --set last last char index Select @Last_Delimit_Index = @Next_Delimit_Index End Return End --sample output Select * from [Split_Text_Into_Table_Multi_Column] (',',' ',' 917064578 70 917581826 66 917635915 66 917663205 66 918320481 66 918752247 ER 918892315 70 919162748 52 919169913 JS 919169927 70 919187495 52 919207137 52 919226194 70 919252530 52 919252544 KG 919269942 52 919269957 KG 919269961 47 ')
Output
row_id|column_1|column_2|column_3 1|917064578|70|NULL 2|917581826|66|NULL 3|917635915|66|NULL 4|917663205|66|NULL 5|918320481|66|NULL 6|918752247|ER|NULL 7|918892315|70|NULL 8|919162748|52|NULL 9|919169913|JS|NULL 10|919169927|70|NULL 11|919187495|52|NULL 12|919207137|52|NULL 13|919226194|70|NULL 14|919252530|52|NULL 15|919252544|KG|NULL 16|919269942|52|NULL 17|919269957|KG|NULL 18|919269961|47|NULL