I have a name field in Students table which is a comma separated string in format "LastName, FirstName, Middle Name".While doing a select statement in SQL query I need to break this up into separate fields.How can I achieve this in SQL?.Some times Middle intial won't be available.
SUBSTRING(Name,CHARINDEX(',',Name,1)+2,LEN(Name)) AS FirstName,
SUBSTRING(Name,1,CHARINDEX(',',Name,1)-1) AS LastName,
Above code works fine when there is no Middle name.
This should give you what you need:
declare #tmp table (fullname varchar(100));
insert #tmp values('James, Billy, L'), ('John, Snow');
select
fullname
, [Last Name]
, case
when charindex(',', Remainder, 0) > 0
then ltrim(substring(Remainder, 0, charindex(',', Remainder, 0)))
else ltrim(Remainder)
end [First Name]
, case
when charindex(',', Remainder, 0) = 0
then NULL
else ltrim(substring(Remainder, charindex(',', Remainder, 0) + 1, len(Remainder)))
end [Middle Name]
from
(select
fullname
, substring(fullname, 0, charindex(',', fullname, 0)) [Last Name]
, substring(fullname, charindex(',', fullname, 0) + 1, len(fullname)) [Remainder]
from #tmp) result;
First just find the occurrences of comma(,) in the string. Then use CASE expression to get the number of comma. If there is 2 comma then we can assume that middle name is also there. If 1 then only first name and last name. Then use the combinations of LEFT, RIGHT, SUBSTRING, CHARINDEX string functions.
Query
select t.name,
left(
t.name,
charindex(',', t.name, 1) - 1
) last_name,
case t.comma_num
when 2
then substring(
t.name,
charindex(',', t.name, 1) + 1,
len(name) -
(charindex(',', t.name, 1) + 1) - charindex(',', reverse(t.name), 1) + 1
)
when 1
then right(
t.name,
charindex(',', reverse(t.name), 1) - 1
)
else null end as first_name,
case t.comma_num
when 2
then right(
t.name, charindex(',', reverse(t.name), 1) - 1
)
else null end as middle_name
from (
select name,
len(name) - len(replace(name, ',', '')) comma_num
from [your_table_name]
)t;
Find demo here
Use CTE and SUBSTRING AND CHARINDEX funntions
DECLARE #Name VARCHAR(100) = 'James, Billy, L'
--DECLARE #Name VARCHAR(100) = 'James, '', L'
;WITH _CTE ( SplitedNames ,RemainStr) AS
(
SELECT SUBSTRING(#Name,0,CHARINDEX(',',#Name)),
SUBSTRING(#Name,CHARINDEX(',',#Name)+1,LEN(#Name))
UNION ALL
SELECT CASE WHEN CHARINDEX(',',RemainStr) = 0 THEN RemainStr ELSE
SUBSTRING(RemainStr,0,CHARINDEX(',',RemainStr)) END,
CASE WHEN CHARINDEX(',',RemainStr) = 0 THEN '' ELSE
SUBSTRING(RemainStr,CHARINDEX(',',RemainStr)+1,LEN(RemainStr))
END
FROM _CTE
WHERE RemainStr <> ''
)
SELECT SplitedNames FROM _CTE
Related
I'm trying to split the first name from the middle name or middle initial when there's an underscore in the name. I was able to split the first name from the middle name when there is a space, but having trouble with the underscore.
I would like to keep it all together in the case statement if possible.
SELECT
[first name]
, SUBSTRING([first name], 1,
CASE WHEN SUBSTRING(REVERSE([first name]), 2, 1) = ' '
THEN CHARINDEX(' ', [first name]) - 1
ELSE LEN([first name])
END) AS FirstName ,
CASE WHEN SUBSTRING(REVERSE([first name]), 2, 1) = ' '
THEN SUBSTRING([first name], LEN([first name]), 1)
ELSE NULL
END AS MiddleName
, [Last Name]
FROM nametable
The following example uses two case expressions to separate one column into two. It makes use of a feature of Substring: no error is raised if the specified length exceeds the length of the input string.
Note that the sample data is not an image of data, but useful data.
declare #Samples as Table ( Name VarChar(20) );
insert into #Samples ( Name ) values
( 'Billy' ), ( 'Billy Bob' ), ( 'Billy_Joe' ), ( 'Edgar_7' ),
( '_' ), ( 'X_' ), ( '_Y' ), ( '' );
select Name,
case
when CharIndex( '_', Name ) > 0 then Left( Name, CharIndex( '_', Name ) - 1 )
else Name end as FirstName,
case
when CharIndex( '_', Name ) > 0 then Substring( Name, CharIndex( '_', Name ) + 1, 20 )
else NULL end as MiddleName
from #Samples;
Add the REPLACE function inline:
SELECT
[first name]
, SUBSTRING([first name], 1,
CASE WHEN SUBSTRING(REVERSE(REPLACE([first name], '_', ' ')), 2, 1) = ' '
THEN CHARINDEX(' ', REPLACE([first name], '_', ' ')) - 1
ELSE LEN([first name])
END) AS FirstName ,
CASE WHEN SUBSTRING(REVERSE(REPLACE([first name], '_', ' ')), 2, 1) = ' '
THEN SUBSTRING(REPLACE([first name], '_', ' '), LEN([first name]), 1)
ELSE NULL
END AS MiddleName
, [Last Name]
FROM nametable
Thanks to sample data provided by #HABO, you can try this too:
SELECT L.Name,
L.FirstName,
CASE L.MiddleName WHEN '' THEN NULL ELSE L.MiddleName END AS MiddleName
FROM
(
SELECT P.Name,
REPLACE(SUBSTRING(P.UnderscoredName, 1, P.UnderscoreIndex), '_', '') AS FirstName,
REPLACE(SUBSTRING(P.UnderscoredName, P.UnderscoreIndex, LEN(P.UnderscoredName) - P.UnderscoreIndex + 1),'_','') AS MiddleName
FROM
(
SELECT K.Name,
K.UnderscoredName,
CHARINDEX('_', K.UnderscoredName) AS UnderscoreIndex
FROM
(
SELECT Name,
REPLACE(CASE WHEN Name LIKE N'%[_]%' THEN Name ELSE Name + '_' END,' ','_') AS UnderscoredName
FROM #Samples
) AS K
) AS P
) AS L;
SELECT
right(name,7),
substring(params, charindex('|-|', params)+3,LEN(params)) as 'List Name',
convert(varchar,dateadd(hh,-8,created_date), 101) as Date,
convert(char, dateadd(hh,-8,created_date), 108) as Time
FROM
[meldb].[dbo].[mr_message]
WHERE
name in ('CL_LIST_STARTED', 'CL_LIST_STOPPED')
AND dateadd(hh,-8,created_date) > '7/1/2014'
ORDER BY
created_date ASC
List name will return something like:
firstname.lastname-|LISTNAME|-|PARENTLISTNAME
I'm trying to isolate LISTNAME and PARENTLISTNAME into separate columns, but since they can vary in char size I can't just specify right or left
Btw I didn't create this table I'm just stuck using it
Any ideas?
Did you try it yet?
Ok... happy friday :)
declare #str varchar(100);
set #str = 'jim.smith|-|firstItem|-|secondItem';
--- for your query, change #str to the column name, obviously ---
select
substring(
#str
, charindex('|-|', #str) + 3
, ( ( charindex('|-|', #str, charindex('|-|', #str) + 3) ) - ( charindex('|-|', #str) + 3) )
)
,substring(
#str
, charindex('|-|', #str, charindex('|-|', #str) + 3) + 3
, len(#str) -- guaranteed to be past the end, to catch all
)
Do you want to split params into three columns? Please check below query.
SELECT
SUBSTRING(params, 1, CHARINDEX('-', params)-1) AS FullName,
SUBSTRING(STUFF(params, CHARINDEX('|-|', params), LEN(params), ''), CHARINDEX('-', params) + 2, LEN(params)) AS 'List Name',
SUBSTRING(params, CHARINDEX('|-|', params) + 3, LEN(params)) AS 'Parent List Name',
CONVERT(VARCHAR,DATEADD(hh,-8,created_date), 101) AS DATE,
CONVERT(CHAR, DATEADD(hh,-8,created_date), 108) AS TIME
FROM
[meldb].[dbo].[mr_message]
WHERE
name IN ('CL_LIST_STARTED', 'CL_LIST_STOPPED')
AND DATEADD(hh,-8,created_date) > '7/1/2014'
ORDER BY
created_date ASC
Here is the format I came up with using PAT index and the separators you mentioned:
SELECT name,
substring(name, 0, charindex('.', name)) as 'FirstName',
substring(name, charindex('.', name) + 1, patindex('%|-|%', name) - patindex('%-|%', name) -2) as 'LastName',
substring(name,patindex('%-|%', name)+2, patindex('%|-|%', name) - patindex('%-|%', name)-2) as 'ListName',
substring(name, patindex('%|-|%', name)+3, len(name) - patindex('%|-|%', name)) as 'ParentListName'
from FancyNames
Link to SQL Fiddle: http://sqlfiddle.com/#!6/03c2c/38
I have some data that I would like to split based on a delimiter that may or may not exist.
Example data:
John/Smith
Jane/Doe
Steve
Bob/Johnson
I am using the following code to split this data into First and Last names:
SELECT SUBSTRING(myColumn, 1, CHARINDEX('/', myColumn)-1) AS FirstName,
SUBSTRING(myColumn, CHARINDEX('/', myColumn) + 1, 1000) AS LastName
FROM MyTable
The results I would like:
FirstName---LastName
John--------Smith
Jane--------Doe
Steve-------NULL
Bob---------Johnson
This code works just fine as long as all the rows have the anticipated delimiter, but errors out when a row does not:
"Invalid length parameter passed to the LEFT or SUBSTRING function."
How can I re-write this to work properly?
May be this will help you.
SELECT SUBSTRING(myColumn, 1, CASE CHARINDEX('/', myColumn)
WHEN 0
THEN LEN(myColumn)
ELSE CHARINDEX('/', myColumn) - 1
END) AS FirstName
,SUBSTRING(myColumn, CASE CHARINDEX('/', myColumn)
WHEN 0
THEN LEN(myColumn) + 1
ELSE CHARINDEX('/', myColumn) + 1
END, 1000) AS LastName
FROM MyTable
For those looking for answers for SQL Server 2016+. Use the built-in STRING_SPLIT function
Eg:
DECLARE #tags NVARCHAR(400) = 'clothing,road,,touring,bike'
SELECT value
FROM STRING_SPLIT(#tags, ',')
WHERE RTRIM(value) <> '';
Reference: https://msdn.microsoft.com/en-nz/library/mt684588.aspx
Try filtering out the rows that contain strings with the delimiter and work on those only like:
SELECT SUBSTRING(myColumn, 1, CHARINDEX('/', myColumn)-1) AS FirstName,
SUBSTRING(myColumn, CHARINDEX('/', myColumn) + 1, 1000) AS LastName
FROM MyTable
WHERE CHARINDEX('/', myColumn) > 0
Or
SELECT SUBSTRING(myColumn, 1, CHARINDEX('/', myColumn)-1) AS FirstName,
SUBSTRING(myColumn, CHARINDEX('/', myColumn) + 1, 1000) AS LastName
FROM MyTable
WHERE myColumn LIKE '%/%'
SELECT CASE
WHEN CHARINDEX('/', myColumn, 0) = 0
THEN myColumn
ELSE LEFT(myColumn, CHARINDEX('/', myColumn, 0)-1)
END AS FirstName
,CASE
WHEN CHARINDEX('/', myColumn, 0) = 0
THEN ''
ELSE RIGHT(myColumn, CHARINDEX('/', REVERSE(myColumn), 0)-1)
END AS LastName
FROM MyTable
ALTER FUNCTION [dbo].[split_string](
#delimited NVARCHAR(MAX),
#delimiter NVARCHAR(100)
) RETURNS #t TABLE (id INT IDENTITY(1,1), 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
I just wanted to give an alternative way to split a string with multiple delimiters, in case you are using a SQL Server version under 2016.
The general idea is to split out all of the characters in the string, determine the position of the delimiters, then obtain substrings relative to the delimiters. Here is a sample:
-- Sample data
DECLARE #testTable TABLE (
TestString VARCHAR(50)
)
INSERT INTO #testTable VALUES
('Teststring,1,2,3')
,('Test')
DECLARE #delimiter VARCHAR(1) = ','
-- Generate numbers with which we can enumerate
;WITH Numbers AS (
SELECT 1 AS N
UNION ALL
SELECT N + 1
FROM Numbers
WHERE N < 255
),
-- Enumerate letters in the string and select only the delimiters
Letters AS (
SELECT n.N
, SUBSTRING(t.TestString, n.N, 1) AS Letter
, t.TestString
, ROW_NUMBER() OVER ( PARTITION BY t.TestString
ORDER BY n.N
) AS Delimiter_Number
FROM Numbers n
INNER JOIN #testTable t
ON n <= LEN(t.TestString)
WHERE SUBSTRING(t.TestString, n, 1) = #delimiter
UNION
-- Include 0th position to "delimit" the start of the string
SELECT 0
, NULL
, t.TestString
, 0
FROM #testTable t
)
-- Obtain substrings based on delimiter positions
SELECT t.TestString
, ds.Delimiter_Number + 1 AS Position
, SUBSTRING(t.TestString, ds.N + 1, ISNULL(de.N, LEN(t.TestString) + 1) - ds.N - 1) AS Delimited_Substring
FROM #testTable t
LEFT JOIN Letters ds
ON t.TestString = ds.TestString
LEFT JOIN Letters de
ON t.TestString = de.TestString
AND ds.Delimiter_Number + 1 = de.Delimiter_Number
OPTION (MAXRECURSION 0)
The examples above work fine when there is only one delimiter, but it doesn't scale well for multiple delimiters. Note that this will only work for SQL Server 2016 and above.
/*Some Sample Data*/
DECLARE #mytable TABLE ([id] VARCHAR(10), [name] VARCHAR(1000));
INSERT INTO #mytable
VALUES ('1','John/Smith'),('2','Jane/Doe'), ('3','Steve'), ('4','Bob/Johnson')
/*Split based on delimeter*/
SELECT P.id, [1] 'FirstName', [2] 'LastName', [3] 'Col3', [4] 'Col4'
FROM(
SELECT A.id, X1.VALUE, ROW_NUMBER() OVER (PARTITION BY A.id ORDER BY A.id) RN
FROM #mytable A
CROSS APPLY STRING_SPLIT(A.name, '/') X1
) A
PIVOT (MAX(A.[VALUE]) FOR A.RN IN ([1],[2],[3],[4],[5])) P
These all helped me get to this. I am still on 2012 but now have something quick that will allow me to split a string, even if string has varying numbers of delimiters, and grab the nth substring from that string. It's quick too. I know this post is old, but it took me forever to find something so hopefully this will help someone else.
CREATE FUNCTION [dbo].[SplitsByIndex]
(#separator VARCHAR(20) = ' ',
#string VARCHAR(MAX),
#position INT
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #results TABLE
(id INT IDENTITY(1, 1),
chrs VARCHAR(8000)
);
DECLARE #outResult VARCHAR(8000);
WITH X(N)
AS (SELECT 'Table1'
FROM(VALUES(0), (0), (0), (0), (0), (0), (0), (0), (0), (0), (0), (0), (0), (0), (0), (0)) T(C)),
Y(N)
AS (SELECT 'Table2'
FROM X A1,
X A2,
X A3,
X A4,
X A5,
X A6,
X A7,
X A8), -- Up to 16^8 = 4 billion
T(N)
AS (SELECT TOP (ISNULL(LEN(#string), 0)) ROW_NUMBER() OVER(
ORDER BY
(
SELECT NULL
)) - 1 N
FROM Y),
Delim(Pos)
AS (SELECT t.N
FROM T
WHERE(SUBSTRING(#string, t.N, LEN(#separator + 'x') - 1) LIKE #separator
OR t.N = 0)),
Separated(value)
AS (SELECT SUBSTRING(#string, d.Pos + LEN(#separator + 'x') - 1, LEAD(d.Pos, 1, 2147483647) OVER(
ORDER BY
(
SELECT NULL
))-d.Pos - LEN(#separator))
FROM Delim d
WHERE #string IS NOT NULL)
INSERT INTO #results(chrs)
SELECT s.value
FROM Separated s
WHERE s.value <> #separator;
SELECT #outResult =
(
SELECT chrs
FROM #results
WHERE id = #position
);
RETURN #outResult;
END;
This can be used like this:
SELECT [dbo].[SplitsByIndex](' ',fieldname,2)
from tablename
I would protect the substring operation by always appending a delimiter to the test strings. This makes the parsing much simpler. Your code may now rely on finding the right pattern, and not need to cope with special cases.
SELECT SUBSTRING(myColumn + '/', 1, CHARINDEX('/', myColumn)-1) AS FirstName,
SUBSTRING(myColumn + '/', CHARINDEX('/', myColumn) + 1, 1000) AS LastName
FROM MyTable
It eliminates edge cases and conditionals and cases.
Always add an extra delimiter at the end, then the challenge case is no problem.
I need to return everything in a string before the space:
select Substring('stack overflow', 1, CharIndex( ' ', 'stack overflow' ) - 1)
this will yield stack
however if we don't have a space in the data, i would like to return the entire string:
select Substring('stackoverflow', 1, CharIndex( ' ', 'stackoverflow' ) - 1)
i would like that to return stackoverflow
what is the correct way to handle this scenario?
;WITH T(C) AS
(
SELECT 'stack overflow' UNION ALL
SELECT 'stackoverflow'
)
SELECT LEFT(C, CharIndex( ' ', C + ' ' ) - 1)
FROM T
Borrowing #MartinSmith's definition:
;WITH T(C) AS
(
SELECT 'stack overflow'
UNION ALL
SELECT 'stackoverflow'
)
SELECT SUBSTRING(C, 1, COALESCE(NULLIF(CHARINDEX(' ', C)-1, -1), 255))
FROM T;
That said, I prefer Martin's. Both avoid checking the length or performing CASE etc., but mine assumes you know the max length of the string (here I assumed 255).
I'm late as per usual; here on SQL Fiddle
CREATE TABLE The_Table
(
TestString varchar(50)
);
INSERT INTO The_Table
(TestString)
VALUES
('stack overflow'),
('stackoverflow');
select
[myResult] = case
when CharIndex( ' ', TestString)> 0 then Substring(TestString, 1, CharIndex( ' ', TestString ) - 1)
when CharIndex( ' ', TestString)= 0 then TestString
else TestString
end
from The_Table
;With T(C) AS
(
Select 'Stack'
Union All
Select 'Stack OverFlow'
)
Select
Case When CharIndex(' ', C) > 0
Then SUBSTRING(C, 0, CharIndex(' ', C))
Else
C
End
From T
Declare #str varchar(100)='stack overflow'
SELECT CASE WHEN CHARINDEX(' ',#str,1) > 0 then LEFT(#str,CHARINDEX(' ',#str,1)) else #str END
I have a problem splitting single column values to multiple column values.
For Example:
Name
------------
abcd efgh
ijk lmn opq
asd j. asdjja
asb (asdfas) asd
asd
and I need the output something like this:
first_name last_name
----------------------------------
abcd efgh
ijk opq
asd asdjja
asb asd
asd null
The middle name can be omitted (no need for a middle name) The columns are already created and need to insert the data from that single Name column.
Your approach won't deal with lot of names correctly but...
SELECT CASE
WHEN name LIKE '% %' THEN LEFT(name, Charindex(' ', name) - 1)
ELSE name
END,
CASE
WHEN name LIKE '% %' THEN RIGHT(name, Charindex(' ', Reverse(name)) - 1)
END
FROM YourTable
An alternative to Martin's
select LEFT(name, CHARINDEX(' ', name + ' ') -1),
STUFF(name, 1, Len(Name) +1- CHARINDEX(' ',Reverse(name)), '')
from somenames
Sample table
create table somenames (Name varchar(100))
insert somenames select 'abcd efgh'
insert somenames select 'ijk lmn opq'
insert somenames select 'asd j. asdjja'
insert somenames select 'asb (asdfas) asd'
insert somenames select 'asd'
insert somenames select ''
insert somenames select null
;WITH Split_Names (Name, xmlname)
AS
(
SELECT
Name,
CONVERT(XML,'<Names><name>'
+ REPLACE(Name,' ', '</name><name>') + '</name></Names>') AS xmlname
FROM somenames
)
SELECT
xmlname.value('/Names[1]/name[1]','varchar(100)') AS first_name,
xmlname.value('/Names[1]/name[2]','varchar(100)') AS last_name
FROM Split_Names
and also check the link below for reference
http://jahaines.blogspot.in/2009/06/converting-delimited-string-of-values.html
What you need is a split user-defined function. With that, the solution looks like
With SplitValues As
(
Select T.Name, Z.Position, Z.Value
, Row_Number() Over ( Partition By T.Name Order By Z.Position ) As Num
From Table As T
Cross Apply dbo.udf_Split( T.Name, ' ' ) As Z
)
Select Name
, FirstName.Value
, Case When ThirdName Is Null Then SecondName Else ThirdName End As LastName
From SplitValues As FirstName
Left Join SplitValues As SecondName
On S2.Name = S1.Name
And S2.Num = 2
Left Join SplitValues As ThirdName
On S2.Name = S1.Name
And S2.Num = 3
Where FirstName.Num = 1
Here's a sample split function:
Create Function [dbo].[udf_Split]
(
#DelimitedList nvarchar(max)
, #Delimiter nvarchar(2) = ','
)
RETURNS TABLE
AS
RETURN
(
With CorrectedList As
(
Select Case When Left(#DelimitedList, Len(#Delimiter)) <> #Delimiter Then #Delimiter Else '' End
+ #DelimitedList
+ Case When Right(#DelimitedList, Len(#Delimiter)) <> #Delimiter Then #Delimiter Else '' End
As List
, Len(#Delimiter) As DelimiterLen
)
, Numbers As
(
Select TOP( Coalesce(DataLength(#DelimitedList)/2,0) ) Row_Number() Over ( Order By c1.object_id ) As Value
From sys.columns As c1
Cross Join sys.columns As c2
)
Select CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen As Position
, Substring (
CL.List
, CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen
, CharIndex(#Delimiter, CL.list, N.Value + 1)
- ( CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen )
) As Value
From CorrectedList As CL
Cross Join Numbers As N
Where N.Value <= DataLength(CL.List) / 2
And Substring(CL.List, N.Value, CL.DelimiterLen) = #Delimiter
)
SELECT
SUBSTRING_INDEX(SUBSTRING_INDEX(rent, ' ', 1), ' ', -1) AS currency,
SUBSTRING_INDEX(SUBSTRING_INDEX(rent, ' ', 3), ' ', -1) AS rent
FROM tolets
I used it recently:
select
substring(name,1,charindex(' ',name)-1) as Col1,
substring(name,charindex(' ',name)+1,len(name)) as Col2
from TableName
Here is how I did this on a SQLite database:
SELECT SUBSTR(name, 1,INSTR(name, " ")-1) as Firstname,
SUBSTR(name, INSTR(name," ")+1, LENGTH(name)) as Lastname
FROM YourTable;
Hope it helps.