Gurru
Gurru

Reputation: 2103

How to split a comma-separated value to columns

I have a table like this

Value String
1 Cleo, Smith

I want to separate the comma delimited string into two columns

Value Name Surname
1 Cleo Smith

I need only two fixed extra columns

Upvotes: 195

Views: 1344725

Answers (30)

Azar
Azar

Reputation: 1867

I think this is cool

SELECT value,
    PARSENAME(REPLACE(String,',','.'),2) 'Name' ,
    PARSENAME(REPLACE(String,',','.'),1) 'Surname'
FROM table 

Upvotes: 52

bvr
bvr

Reputation: 4826

;WITH Split_Names (Value,Name, xmlname)
AS
(
    SELECT Value,
    Name,
    CONVERT(XML,'<Names><name>'  
    + REPLACE(Name,',', '</name><name>') + '</name></Names>') AS xmlname
      FROM tblnames
)

 SELECT Value,      
 xmlname.value('(/Names/name/text())[1]','varchar(100)') AS Name,    
 xmlname.value('(/Names/name/text())[2]','varchar(100)') AS Surname
 FROM Split_Names

and also check the link below for reference

http://jahaines.blogspot.in/2009/06/converting-delimited-string-of-values.html

Upvotes: 70

Romil Kumar Jain
Romil Kumar Jain

Reputation: 20775

Your purpose can be solved using the following query:

select Value, 
    Substring(FullName, 1, Charindex(',', FullName)-1) as Name,
    Substring(FullName, Charindex(',', FullName)+1, LEN(FullName)) as  Surname
from Table1

There is no readymade Split function in SQL Server, so we need to create a user defined function.

CREATE FUNCTION Split (
      @InputString                  VARCHAR(8000),
      @Delimiter                    VARCHAR(50)
)
 
RETURNS @Items TABLE (
      Item                          VARCHAR(8000)
)
 
AS
BEGIN
      IF @Delimiter = ' '
      BEGIN
            SET @Delimiter = ','
            SET @InputString = REPLACE(@InputString, ' ', @Delimiter)
      END
 
      IF (@Delimiter IS NULL OR @Delimiter = '')
            SET @Delimiter = ','
 
--INSERT INTO @Items VALUES (@Delimiter) -- Diagnostic
--INSERT INTO @Items VALUES (@InputString) -- Diagnostic
 
      DECLARE @Item           VARCHAR(8000)
      DECLARE @ItemList       VARCHAR(8000)
      DECLARE @DelimIndex     INT
 
      SET @ItemList = @InputString
      SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0)
      WHILE (@DelimIndex != 0)
      BEGIN
            SET @Item = SUBSTRING(@ItemList, 0, @DelimIndex)
            INSERT INTO @Items VALUES (@Item)
 
            -- Set @ItemList = @ItemList minus one less item
            SET @ItemList = SUBSTRING(@ItemList, @DelimIndex+1, LEN(@ItemList)-@DelimIndex)
            SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0)
      END -- End WHILE
 
      IF @Item IS NOT NULL -- At least one delimiter was encountered in @InputString
      BEGIN
            SET @Item = @ItemList
            INSERT INTO @Items VALUES (@Item)
      END
 
      -- No delimiters were encountered in @InputString, so just return @InputString
      ELSE INSERT INTO @Items VALUES (@InputString)
 
      RETURN
 
END -- End Function
GO
 
---- Set Permissions
--GRANT SELECT ON Split TO UserRole1
--GRANT SELECT ON Split TO UserRole2
--GO

Upvotes: 168

Paulo Moreira
Paulo Moreira

Reputation: 631

I did:

drop table if exists #test;
create table #test(valor varchar(200));
insert into #test values ('Cleo, Smith'), ('Jhon');
select
    *
    ,REVERSE(PARSENAME(REPLACE(REVERSE(valor), ',', '.'), 1)) as name
     ,REVERSE(PARSENAME(REPLACE(REVERSE(valor), ',', '.'), 2)) as Surname
    ,REVERSE(PARSENAME(REPLACE(REVERSE(valor), ',', '.'), 3)) as other
from #test;

/*
+-----------+----+-------+-----+
|valor      |name|Surname|other|
+-----------+----+-------+-----+
|Cleo, Smith|Cleo| Smith |NULL |
|Jhon       |Jhon|NULL   |NULL |
+-----------+----+-------+-----+
*/

Upvotes: 1

Masoud Safari
Masoud Safari

Reputation: 78

You can use SQL Server STRING_SPLIT function:

STRING_SPLIT ( string , separator )  

Upvotes: 1

aads
aads

Reputation: 2495

xml based answer is simple and clean

refer this

DECLARE @S varchar(max),
        @Split char(1),
        @X xml

SELECT @S = 'ab,cd,ef,gh,ij',
       @Split = ','

SELECT @X = CONVERT(xml,' <root> <myvalue>' +
REPLACE(@S,@Split,'</myvalue> <myvalue>') + '</myvalue>   </root> ')

SELECT  T.c.value('.','varchar(20)'),              --retrieve ALL values at once
  T.c.value('(/root/myvalue)[1]','VARCHAR(20)')  , --retrieve index 1 only, which is the 'ab'
  T.c.value('(/root/myvalue)[2]','VARCHAR(20)')
 FROM @X.nodes('/root/myvalue') T(c)

Upvotes: 53

Zhorov
Zhorov

Reputation: 30023

It's an old question, but if an upgrade to SQL Server 2017+ is possible, a JSON-based approach is also an option. The idea is to make an appropriate transformation:

  • Transform the text stored in the String column into a valid JSON array (Cleo, Smith into ["Cleo"," Smith"]) and parse this array with JSON_VALUE().

  • Transform the text stored in the String column into a valid nested JSON array (Cleo, Smith into [["Cleo"," Smith"]]) and parse this array with OPENJSON() and explicit schema (columns definitions).

Table:

SELECT [Value], [String]
INTO Data
FROM (VALUES
   (1, 'Cleo, Smith'),
   (2, 'John, Smith'),
   (3, 'Marian')
) v ([Value], [String])

Statement using JSON_VALUE():

SELECT 
   [Value], 
   TRIM(JSON_VALUE(CONCAT('["', REPLACE(STRING_ESCAPE([String], 'json'), ',', '","'), '"]'), 'lax $[0]')) AS Name,
   TRIM(JSON_VALUE(CONCAT('["', REPLACE(STRING_ESCAPE([String], 'json'), ',', '","'), '"]'), 'lax $[1]')) AS Surname
FROM Data

Statement using OPENJSON():

SELECT d.[Value], TRIM(j.[Name]) AS [Name], TRIM(j.[Surname]) AS [Surname]
FROM Data d
OUTER APPLY OPENJSON(CONCAT('[["', REPLACE(STRING_ESCAPE(d.[String], 'json'), ',', '","'), '"]]')) WITH (
   Name varchar(100) 'lax $[0]',
   Surname varchar(100) 'lax $[1]'
) j

Result:

Value  Name   Surname
---------------------
1      Cleo   Smith
2      John   Smith
3      Marian   

As an additional note, with this technique you can easily parse the text with more than two columns by adding the appropariate JSON path.

Upvotes: 0

bwanamaina
bwanamaina

Reputation: 309

You can use a table-valued function STRING_SPLIT, which is available only under compatibility level 130. If your database compatibility level is lower than 130, SQL Server will not be able to find and execute the STRING_SPLIT function. You can change a compatibility level of the database using the following command:

ALTER DATABASE DatabaseName SET COMPATIBILITY_LEVEL = 130

Syntax

SELECT * FROM STRING_SPLIT ( string, separator )

see documentation here

Upvotes: 9

ammad khan
ammad khan

Reputation: 1154

Try this out

CREATE FUNCTION [dbo].[Split]  
(  
 @ListOfValues varchar(max),   
 @ValueSeparator varchar(10)  
)  
RETURNS @ListOfValuesInRows TABLE  
(  
 Value varchar(max)  
)  
AS  
BEGIN  

 IF Len(@ListOfValues) = 0  
  RETURN   

 if @ValueSeparator <> ' '  
 Begin  
  WHILE CHARINDEX(@ValueSeparator, @ListOfValues) > 0  
  BEGIN  

   INSERT INTO @ListOfValuesInRows   
   SELECT LTRIM(RTRIM(SUBSTRING(@ListOfValues, 1, CHARINDEX(@ValueSeparator, @ListOfValues)-1)))  

   SET @ListOfValues = SubString(@ListOfValues, CharIndex(@ValueSeparator, @ListOfValues)+Len(@ValueSeparator), Len(@ListOfValues))  

  END  

  INSERT INTO @ListOfValuesInRows  
  SELECT LTRIM(RTRIM(@ListOfValues))  
 End  
 Else  
 BEGIN  
  DECLARE @xml XML;  
  SET @xml = N'<t>' + REPLACE(@ListOfValues, @ValueSeparator, '</t><t>') + '</t>';  
  INSERT INTO @ListOfValuesInRows (Value)  
  SELECT LTRIM(RTRIM(r.value( '.', 'varchar(MAX)' ))) AS item  
  FROM @xml.nodes( '/t' ) AS records( r )  

 END  

RETURN  

END  

Upvotes: -2

parfilko
parfilko

Reputation: 1454

question is simple, but problem is hot :)

So I create some wrapper for string_split() which pivot result in more generic way. It's table function which returns values (nn, value1, value2, ... , value50) - enough for most CSV lines. If there are more values, they will wrap to next line - nn indicate line number. Set third parameter @columnCnt = [yourNumber] to wrap at specific position:

alter FUNCTION fn_Split50
(   
    @str varchar(max),
    @delim char(1), 
    @columnCnt int = 50
)
RETURNS TABLE 
AS
RETURN 
(
    SELECT * 
    FROM (SELECT 
            nn = (nn - 1) / @columnCnt + 1, 
            nnn = 'value' + cast(((nn - 1) % @columnCnt) + 1 as varchar(10)), 
            value
        FROM (SELECT 
            nn = ROW_NUMBER() over (order by (select null)),
            value
            FROM string_split(@str, @delim) aa
            ) aa
        where nn > 0
    ) bb
    PIVOT  
    (  
    max(value) 
    FOR nnn IN (    
        value1, value2, value3, value4, value5, value6, value7, value8, value9, value10,
        value11, value12, value13, value14, value15, value16, value17, value18, value19, value20,
        value21, value22, value23, value24, value25, value26, value27, value28, value29, value30,
        value31, value32, value33, value34, value35, value36, value37, value38, value39, value40,
        value41, value42, value43, value44, value45, value46, value47, value48, value49, value50        
     )  
    ) AS PivotTable 
)

Example of using:

select * from dbo.fn_split50('zz1,aa2,ss3,dd4,ff5', ',', DEFAULT)

result 1

select * from dbo.fn_split50('zz1,aa2,ss3,dd4,ff5,gg6,hh7,jj8,ww9,qq10', ',', 3)

enter image description here

select * from dbo.fn_split50('zz1,11,aa2,22,ss3,33,dd4,44,ff5,55,gg6,66,hh7,77,jj8,88,ww9,99,qq10,1010', ',',2)

enter image description here

Hope, it will helps :)

Upvotes: 4

URMIL PREMAL SHAH
URMIL PREMAL SHAH

Reputation: 17

Try below:

USE TRIAL
GO
CREATE TABLE DETAILS
(
  ID INT,
  NAME VARCHAR(50),
  ADDRESS VARCHAR(50)
)

INSERT INTO DETAILS
VALUES (100, 'POPE-JOHN-PAUL','VATICAN CIT|ROME|ITALY')
,(240, 'SIR-PAUL-McARTNEY','NEWYORK CITY|NEWYORK|USA')
,(460,'BARRACK-HUSSEIN-OBAMA','WHITE HOUSE|WASHINGTON|USA')
,(700, 'PRESIDENT-VLADAMIR-PUTIN','RED SQUARE|MOSCOW|RUSSIA')
,(950, 'NARENDRA-DAMODARDAS-MODI','10 JANPATH|NEW DELHI|INDIA')

Query:

select [ID]
,[NAME]
,[ADDRESS]
,REPLACE(LEFT(NAME, CHARINDEX('-', NAME)),'-',' ') as First_Name
,CASE 
WHEN CHARINDEX('-',REVERSE(NAME))+ CHARINDEX('-',NAME) < LEN(NAME)
THEN  SUBSTRING(NAME, CHARINDEX('-', (NAME)) + 1, LEN(NAME) - CHARINDEX('-', REVERSE(NAME)) - CHARINDEX('-', NAME))
ELSE 'NULL'
END AS Middle_Name
,REPLACE(REVERSE( SUBSTRING( REVERSE(NAME), 1, CHARINDEX('-',REVERSE(NAME)))), '-','') AS Last_Name 
,REPLACE(LEFT(ADDRESS, CHARINDEX('|', ADDRESS)),'|',' ') AS Locality
,CASE 
WHEN CHARINDEX('|',REVERSE(ADDRESS))+ CHARINDEX('|',ADDRESS) < LEN(ADDRESS) 
THEN SUBSTRING(ADDRESS, CHARINDEX('|', (ADDRESS))+1, LEN(ADDRESS)-CHARINDEX('|', REVERSE(ADDRESS))-CHARINDEX('|',ADDRESS))
ELSE 'Null' 
END AS STATE
,REPLACE(REVERSE(SUBSTRING(REVERSE(ADDRESS),1 ,CHARINDEX('|',REVERSE(ADDRESS)))),'|','') AS Country
FROM DETAILS

SELECT CHARINDEX('-', REVERSE(NAME)) AS LAST,CHARINDEX('-',NAME)AS FIRST, LEN(NAME) AS LENGTH
FROM DETAILS

SELECT SUBSTRING(NAME, CHARINDEX('-', (NAME))+1, LEN(NAME) -CHARINDEX('-', REVERSE(NAME)) - CHARINDEX('-', NAME))
FROM DETAILS

LET ME KNOW IF YOU HAVE ANY DOUBTS UNDERSTANDING THE CODE

Upvotes: -1

Pete Alvin
Pete Alvin

Reputation: 4800

I re-wrote an answer above and made it better:

CREATE FUNCTION [dbo].[CSVParser]
(
  @s        VARCHAR(255),
  @idx      NUMERIC
)
RETURNS VARCHAR(12)
BEGIN
    DECLARE @comma int
    SET @comma = CHARINDEX(',', @s)
    WHILE 1=1
    BEGIN
        IF @comma=0
            IF @idx=1
                RETURN @s
            ELSE
                RETURN ''

        IF @idx=1
        BEGIN
            DECLARE @word VARCHAR(12)
            SET @word=LEFT(@s, @comma - 1)
            RETURN @word
        END

        SET @s = RIGHT(@s,LEN(@s)-@comma)
        SET @comma = CHARINDEX(',', @s)
        SET @idx = @idx - 1
    END
    RETURN 'not used'
END

Example usage:

SELECT dbo.CSVParser(COLUMN, 1),
       dbo.CSVParser(COLUMN, 2),
       dbo.CSVParser(COLUMN, 3)
FROM   TABLE

Upvotes: 3

anonymous
anonymous

Reputation: 195

SELECT id,
       Substring(NAME, 0, Charindex(',', NAME))             AS firstname,
       Substring(NAME, Charindex(',', NAME), Len(NAME) + 1) AS lastname
FROM   spilt  

Upvotes: 17

Blixter
Blixter

Reputation: 326

CREATE FUNCTION [dbo].[fn_split_string_to_column] (
    @string NVARCHAR(MAX),
    @delimiter CHAR(1)
    )
RETURNS @out_put TABLE (
    [column_id] INT IDENTITY(1, 1) NOT NULL,
    [value] NVARCHAR(MAX)
    )
AS
BEGIN
    DECLARE @value NVARCHAR(MAX),
        @pos INT = 0,
        @len INT = 0

    SET @string = CASE 
            WHEN RIGHT(@string, 1) != @delimiter
                THEN @string + @delimiter
            ELSE @string
            END

    WHILE CHARINDEX(@delimiter, @string, @pos + 1) > 0
    BEGIN
        SET @len = CHARINDEX(@delimiter, @string, @pos + 1) - @pos
        SET @value = SUBSTRING(@string, @pos, @len)

        INSERT INTO @out_put ([value])
        SELECT LTRIM(RTRIM(@value)) AS [column]

        SET @pos = CHARINDEX(@delimiter, @string, @pos + @len) + 1
    END

    RETURN
END

Upvotes: 18

vignesh
vignesh

Reputation: 1454

ALTER function get_occurance_index(@delimiter varchar(1),@occurence int,@String varchar(100))
returns int
AS Begin
--Declare @delimiter varchar(1)=',',@occurence int=2,@String varchar(100)='a,b,c'
Declare @result int
 ;with T as (
    select 1 Rno,0 as row, charindex(@delimiter, @String) pos,@String st
    union all
    select Rno+1,pos + 1, charindex(@delimiter, @String, pos + 1), @String
    from T
    where pos > 0
)
select  @result=pos 
from T 
where pos > 0   and rno = @occurence 
return isnull(@result,0)
ENd


declare @data as table (data varchar(100))
insert into @data values('1,2,3') 
insert into @data values('aaa,bbbbb,cccc') 
select top  3 Substring (data,0,dbo.get_occurance_index( ',',1,data)) ,--First Record always starts with 0
Substring (data,dbo.get_occurance_index( ',',1,data)+1,dbo.get_occurance_index( ',',2,data)-dbo.get_occurance_index( ',',1,data)-1) ,
Substring (data,dbo.get_occurance_index( ',',2,data)+1,len(data)) , -- Last record cant be more than len of actual data
data 
From @data 

Upvotes: 3

Mariano Sedano
Mariano Sedano

Reputation: 85

This function is most fast:

CREATE FUNCTION dbo.F_ExtractSubString
(
  @String VARCHAR(MAX),
  @NroSubString INT,
  @Separator VARCHAR(5)
)
RETURNS VARCHAR(MAX) AS
BEGIN
    DECLARE @St INT = 0, @End INT = 0, @Ret VARCHAR(MAX)
    SET @String = @String + @Separator
    WHILE CHARINDEX(@Separator, @String, @End + 1) > 0 AND @NroSubString > 0
    BEGIN
        SET @St = @End + 1
        SET @End = CHARINDEX(@Separator, @String, @End + 1)
        SET @NroSubString = @NroSubString - 1
    END
    IF @NroSubString > 0
        SET @Ret = ''
    ELSE
        SET @Ret = SUBSTRING(@String, @St, @End - @St)
    RETURN @Ret
END
GO

Example usage:

SELECT dbo.F_ExtractSubString(COLUMN, 1, ', '),
       dbo.F_ExtractSubString(COLUMN, 2, ', '),
       dbo.F_ExtractSubString(COLUMN, 3, ', ')
FROM   TABLE

Upvotes: 6

Michael Schnerring
Michael Schnerring

Reputation: 3661

You may find the solution in SQL User Defined Function to Parse a Delimited String helpful (from The Code Project).

This is the code part from this page:

CREATE FUNCTION [fn_ParseText2Table]
  (@p_SourceText VARCHAR(MAX)
  ,@p_Delimeter VARCHAR(100)=',' --default to comma delimited.
  )
 RETURNS @retTable
  TABLE([Position] INT IDENTITY(1,1)
   ,[Int_Value] INT
   ,[Num_Value] NUMERIC(18,3)
   ,[Txt_Value] VARCHAR(MAX)
   ,[Date_value] DATETIME
   )
AS
/*
********************************************************************************
Purpose: Parse values from a delimited string
  & return the result as an indexed table
Copyright 1996, 1997, 2000, 2003 Clayton Groom (<A href="mailto:[email protected]">[email protected]</A>)
Posted to the public domain Aug, 2004
2003-06-17 Rewritten as SQL 2000 function.
 Reworked to allow for delimiters > 1 character in length
 and to convert Text values to numbers
2016-04-05 Added logic for date values based on "new" ISDATE() function, Updated to use XML approach, which is more efficient.
********************************************************************************
*/


BEGIN
 DECLARE @w_xml xml;
 SET @w_xml = N'<root><i>' + replace(@p_SourceText, @p_Delimeter,'</i><i>') + '</i></root>';


 INSERT INTO @retTable
     ([Int_Value]
    , [Num_Value]
    , [Txt_Value]
    , [Date_value]
     )
     SELECT CASE
       WHEN ISNUMERIC([i].value('.', 'VARCHAR(MAX)')) = 1
       THEN CAST(CAST([i].value('.', 'VARCHAR(MAX)') AS NUMERIC) AS INT)
      END AS [Int_Value]
    , CASE
       WHEN ISNUMERIC([i].value('.', 'VARCHAR(MAX)')) = 1
       THEN CAST([i].value('.', 'VARCHAR(MAX)') AS NUMERIC(18, 3))
      END AS [Num_Value]
    , [i].value('.', 'VARCHAR(MAX)') AS [txt_Value]
    , CASE
       WHEN ISDATE([i].value('.', 'VARCHAR(MAX)')) = 1
       THEN CAST([i].value('.', 'VARCHAR(MAX)') AS DATETIME)
      END AS [Num_Value]
     FROM @w_xml.nodes('//root/i') AS [Items]([i]);
 RETURN;
END;
GO

Upvotes: 3

Muhammad Awais
Muhammad Awais

Reputation: 4502

I think following function will work for you:

You have to create a function in SQL first. Like this

CREATE FUNCTION [dbo].[fn_split](
@str VARCHAR(MAX),
@delimiter CHAR(1)
)
RETURNS @returnTable TABLE (idx INT PRIMARY KEY IDENTITY, item VARCHAR(8000))
AS
BEGIN
DECLARE @pos INT
SELECT @str = @str + @delimiter
WHILE LEN(@str) > 0 
    BEGIN
        SELECT @pos = CHARINDEX(@delimiter,@str)
        IF @pos = 1
            INSERT @returnTable (item)
                VALUES (NULL)
        ELSE
            INSERT @returnTable (item)
                VALUES (SUBSTRING(@str, 1, @pos-1))
        SELECT @str = SUBSTRING(@str, @pos+1, LEN(@str)-@pos)       
    END
RETURN
END

You can call this function, like this:

select * from fn_split('1,24,5',',')

Implementation:

Declare @test TABLE (
ID VARCHAR(200),
Data VARCHAR(200)
)

insert into @test 
(ID, Data)
Values
('1','Cleo,Smith')


insert into @test 
(ID, Data)
Values
('2','Paul,Grim')

select ID,
(select item from fn_split(Data,',') where idx in (1)) as Name ,
(select item from fn_split(Data,',') where idx in (2)) as Surname
 from @test

Result will like this:

enter image description here

Upvotes: 8

user7678586
user7678586

Reputation: 19

Select distinct PROJ_UID,PROJ_NAME,RES_UID from E2E_ProjectWiseTimesheetActuals
where   CHARINDEX(','+cast(PROJ_UID as varchar(8000))+',', @params) > 0 and  CHARINDEX(','+cast(RES_UID as varchar(8000))+',', @res) > 0

Upvotes: 1

Narendra gudapati
Narendra gudapati

Reputation: 47

DECLARE @INPUT VARCHAR (MAX)='N,A,R,E,N,D,R,A'
DECLARE @ELIMINATE_CHAR CHAR (1)=','
DECLARE @L_START INT=1
DECLARE @L_END INT=(SELECT LEN (@INPUT))
DECLARE @OUTPUT CHAR (1)

WHILE @L_START <=@L_END
BEGIN
    SET @OUTPUT=(SUBSTRING (@INPUT,@L_START,1))
    IF @OUTPUT!=@ELIMINATE_CHAR
    BEGIN
        PRINT @OUTPUT
    END
    SET @L_START=@L_START+1
END

Upvotes: 3

vicky
vicky

Reputation: 1580

You can use split function.

SELECT 
(select top 1 item from dbo.Split(FullName,',') where id=1 ) as Name,
(select top 1 item from dbo.Split(FullName,',') where id=2 ) as Surname,
FROM MyTbl

Upvotes: 0

Krishna
Krishna

Reputation: 8556

This worked for me

CREATE FUNCTION [dbo].[SplitString](
    @delimited NVARCHAR(MAX),
    @delimiter NVARCHAR(100)
) RETURNS @t TABLE ( val NVARCHAR(MAX))
AS
BEGIN
    DECLARE @xml XML
    SET @xml = N'<t>' + REPLACE(@delimited,@delimiter,'</t><t>') + '</t>'
    INSERT INTO @t(val)
    SELECT  r.value('.','varchar(MAX)') as item
    FROM  @xml.nodes('/t') as records(r)
    RETURN
END

Upvotes: 4

user7347410
user7347410

Reputation: 183

Try this (change instances of ' ' to ',' or whatever delimiter you want to use)

CREATE FUNCTION dbo.Wordparser
(
  @multiwordstring VARCHAR(255),
  @wordnumber      NUMERIC
)
returns VARCHAR(255)
AS
  BEGIN
      DECLARE @remainingstring VARCHAR(255)
      SET @remainingstring=@multiwordstring

      DECLARE @numberofwords NUMERIC
      SET @numberofwords=(LEN(@remainingstring) - LEN(REPLACE(@remainingstring, ' ', '')) + 1)

      DECLARE @word VARCHAR(50)
      DECLARE @parsedwords TABLE
      (
         line NUMERIC IDENTITY(1, 1),
         word VARCHAR(255)
      )

      WHILE @numberofwords > 1
        BEGIN
            SET @word=LEFT(@remainingstring, CHARINDEX(' ', @remainingstring) - 1)

            INSERT INTO @parsedwords(word)
            SELECT @word

            SET @remainingstring= REPLACE(@remainingstring, Concat(@word, ' '), '')
            SET @numberofwords=(LEN(@remainingstring) - LEN(REPLACE(@remainingstring, ' ', '')) + 1)

            IF @numberofwords = 1
              BREAK

            ELSE
              CONTINUE
        END

      IF @numberofwords = 1
        SELECT @word = @remainingstring
      INSERT INTO @parsedwords(word)
      SELECT @word

      RETURN
        (SELECT word
         FROM   @parsedwords
         WHERE  line = @wordnumber)

  END

Example usage:

SELECT dbo.Wordparser(COLUMN, 1),
       dbo.Wordparser(COLUMN, 2),
       dbo.Wordparser(COLUMN, 3)
FROM   TABLE

Upvotes: 15

Frank
Frank

Reputation: 515

select distinct modelFileId,F4.*
from contract
cross apply (select XmlList=convert(xml, '<x>'+replace(modelFileId,';','</x><x>')+'</x>').query('.')) F2
cross apply (select mfid1=XmlNode.value('/x[1]','varchar(512)')
,mfid2=XmlNode.value('/x[2]','varchar(512)')
,mfid3=XmlNode.value('/x[3]','varchar(512)')
,mfid4=XmlNode.value('/x[4]','varchar(512)') from XmlList.nodes('x') F3(XmlNode)) F4
where modelFileId like '%;%'
order by modelFileId

Upvotes: 1

Kannan Kandasamy
Kannan Kandasamy

Reputation: 13969

With SQL Server 2016 we can use string_split to accomplish this:

create table commasep (
 id int identity(1,1)
 ,string nvarchar(100) )

insert into commasep (string) values ('John, Adam'), ('test1,test2,test3')

select id, [value] as String from commasep 
 cross apply string_split(string,',')

Upvotes: 24

amin.sanjar2
amin.sanjar2

Reputation: 21

ALTER FUNCTION [dbo].[StringListTo] (@StringList Nvarchar(max),@Separators char(1),@start int, @index int )
RETURNS nvarchar(max)
AS
BEGIN
declare @out Nvarchar(max)
declare @i int
declare @start_old int
set @start=@start+1
set @i=1
while(@i<=@index)
begin
    set @start_old=@start
    set @start=CHARINDEX('.',@StringList,@start+1)
    if (@start>0)
    begin
        set @out=Substring(@StringList,@start_old+1,@start-@start_old-1)
    end
else
begin
    set @out=Substring(@StringList,@start_old+1,len(@StringList)-1)
end
set @i=@i+1
end
RETURN @out
END;

Upvotes: -2

Rangani
Rangani

Reputation: 216

Try this:

declare @csv varchar(100) ='aaa,bb,csda,daass';
set @csv = @csv+',';

with cte as
(
    select SUBSTRING(@csv,1,charindex(',',@csv,1)-1) as val, SUBSTRING(@csv,charindex(',',@csv,1)+1,len(@csv)) as rem 
    UNION ALL
    select SUBSTRING(a.rem,1,charindex(',',a.rem,1)-1)as val, SUBSTRING(a.rem,charindex(',',a.rem,1)+1,len(A.rem)) 
    from cte a where LEN(a.rem)>=1
    ) select val from cte

Upvotes: 7

Mehdi najafian
Mehdi najafian

Reputation: 39

it is so easy, you can take it by below query:

DECLARE @str NVARCHAR(MAX)='ControlID_05436b78-04ba-9667-fa01-9ff8c1b7c235,3'
SELECT LEFT(@str, CHARINDEX(',',@str)-1),RIGHT(@str,LEN(@str)-(CHARINDEX(',',@str)))

Upvotes: 2

JasonP
JasonP

Reputation: 221

I think PARSENAME is the neat function to use for this example, as described in this article: http://www.sqlshack.com/parsing-and-rotating-delimited-data-in-sql-server-2012/

The PARSENAME function is logically designed to parse four-part object names. The nice thing about PARSENAME is that it’s not limited to parsing just SQL Server four-part object names – it will parse any function or string data that is delimited by dots.

The first parameter is the object to parse, and the second is the integer value of the object piece to return. The article is discussing parsing and rotating delimited data - company phone numbers, but it can be used to parse name/surname data also.

Example:

USE COMPANY;
SELECT PARSENAME('Whatever.you.want.parsed',3) AS 'ReturnValue';

The article also describes using a Common Table Expression (CTE) called ‘replaceChars’, to run PARSENAME against the delimiter-replaced values. A CTE is useful for returning a temporary view or result set.

After that, the UNPIVOT function has been used to convert some columns into rows; SUBSTRING and CHARINDEX functions have been used for cleaning up the inconsistencies in the data, and the LAG function (new for SQL Server 2012) has been used in the end, as it allows referencing of previous records.

Upvotes: 15

Glyn
Glyn

Reputation: 9

CREATE FUNCTION [dbo].[fnSplit](@sInputList VARCHAR(8000), @sDelimiter VARCHAR(8000) = ',')
RETURNS @List TABLE (item VARCHAR(8000))
BEGIN

    DECLARE @sItem VARCHAR(8000)
    WHILE CHARINDEX(@sDelimiter, @sInputList, 0) <> 0
    BEGIN

        SELECT @sItem = RTRIM(LTRIM(SUBSTRING(@sInputList, 1, CHARINDEX(@sDelimiter, @sInputList,0) - 1))),
               @sInputList = RTRIM(LTRIM(SUBSTRING(@sInputList, CHARINDEX(@sDelimiter, @sInputList, 0) + LEN(@sDelimiter),LEN(@sInputList))))

        -- Indexes to keep the position of searching
        IF LEN(@sItem) > 0

        INSERT INTO @List SELECT @sItem

    END

    IF LEN(@sInputList) > 0
    BEGIN

        INSERT INTO @List SELECT @sInputList -- Put the last item in

    END

    RETURN

END

Upvotes: 0

Related Questions