Splitting a Column into various columns - sql

-- Create table dbo.FullNameTest
IF OBJECT_ID('FullNameTest') IS NOT NULL BEGIN
DROP TABLE dbo.FullNameTest;
END;
CREATE TABLE FullNameTest
(
ID INT Not Null IDENTITY
, FullName NVARCHAR(80) Not Null
, CONSTRAINT PK_ID PRIMARY KEY CLUSTERED(ID)
);
GO
INSERT INTO FullNameTest
VALUES('Mr Hog Finn Gad'), ('Grace Bruce'), ('Dr.Paul'), ('Master Clark James'), ('Mrs.Rignald')
SELECT * FROM FullNameTest
Rules
Treat entries (Mrs, Mr, Miss etc) as Titles and ignore the dot after title
When we have title and a single name, return it as Title, LastName
When we have title and two names return it as Title, FirstName and LastName
When we have title and three names, return Title, FirstName and treat the rest as LastName
When we have no title, reurn data as FirstName, LastName
No function required.
So we can get values in this form:
Title FirstName LastName
------------------------------
Mr Hog Finn Gad
Null Grace Bruce
Dr Null Paul
Master Clark James
Mrs Null Rignald
Thank you

EDIT: ***To test the results yourself, use the following to create your full name table, then run the code to create the title table, and then run the solution code below that. The results are exactly what you asked for.
CREATE TABLE #FullNameTest
(
ID INT Not Null IDENTITY
, FullName NVARCHAR(80) Not Null
);
GO
INSERT INTO #FullNameTest
VALUES('Mr Hog Finn Gad'), ('Grace Bruce'), ('Dr.Paul'), ('Master Clark James'), ('Mrs.Rignald')
This may not work in every situation, and it requires the use of a reference table with Titles, but it should get you started. Again, I would suggest putting in some data validation restraints so you don't get unexpected input. If that's not possible, then run some algorithms to clean your data first. It gets messy when you try to do stuff like this in SQL.
Create a title reference table with title and length of title.
create table #title (Title varchar(15), Long as len(Title))
insert into #title (Title) values
('Mr'),
('Mister'),
('Ms'),
('Miss'),
('Mrs'),
('Misses'),
('Dr'),
('Doctor'),
('Senator'),
('Officer'),
('Master')
Your table looks like this:
Mr 2
Mister 6
Ms 2
Miss 4
Mrs 3
Misses 6
Dr 2
Doctor 6
Senator 7
Officer 7
Master 6
Then you've got this monstrous block of code that attempts to account for every case of input you may have. AGAIN - Try to clean your data first if possible. Otherwise you will have to modify this code every time you receive something unexpected.
select y.RawName
, y.Title
, y.FName
, case when RawName like '%'+y.Title+'% %'+y.FName+'%' then RIGHT(RawName, len(RawName)-LEN(y.Title+'% %'+y.FName+' ')+1)
when Title is not null then RIGHT(RawName, len(RawName)-len(title))
when Title is null and RawName like '% %' then RIGHT(RawName, len(RawName)-len(Fname)-1)
end LName
from (
select RawName
, Title
, case when right(RawName, len(RawName)-len(title)-1) like '% %'
then left(right(RawName, len(RawName)-len(title)-1), CHARINDEX(' ', right(RawName, len(RawName)-len(title)-1),1))
when RawName like '% %'
then left(RawName, CHARINDEX(' ', RawName, 1))
end FName
from (
select
replace(f.FullName, '.', '') RawName
, t.title Title
, ROW_NUMBER() over(partition by f.fullname order by len(t.title) desc) row_n
from #FullNameTest f
left join #title t
on left(f.fullname, t.long) = t.title
) x
where row_n = 1
) y
Based on your samples from above, you are left with the following:
RawName Title FName LName
DrPaul Dr NULL Paul
Grace Bruce NULL Grace Bruce
Master Clark James Master Clark James
Mr Hog Finn Gad Mr Hog Finn Gad
MrsRignald Mrs NULL Rignald
If this is helps you with your problem, please accept it as the answer. :)

Below is the code I eventually used:
SELECT Title,
CASE
WHEN CHARINDEX(' ',PARTNAME) = 0 THEN NULL
ELSE LEFT(PARTNAME, CHARINDEX(' ',PARTNAME))
END AS [FirstName],
CASE WHEN CHARINDEX(' ',PARTNAME) = 0 THEN PARTNAME
ELSE RIGHT(PARTNAME, LEN(PARTNAME) - CHARINDEX(' ', PARTNAME))
END AS [LastName]
FROM
(
SELECT TITLE, LTRIM(REPLACE(REPLACE(FULLNAME,ISNULL(TITLE,''),''),'.','')) AS PARTNAME
FROM (
SELECT CASE
WHEN CHARINDEX('DR ',FullName) = 1 THEN 'Dr'
WHEN CHARINDEX('DR.',FullName) = 1 THEN 'Dr'
WHEN CHARINDEX('MR ',FullName) = 1 THEN 'Mr'
WHEN CHARINDEX('MR.',FullName) = 1 THEN 'Mr'
WHEN CHARINDEX('MRS ',FullName) = 1 THEN 'Mrs'
WHEN CHARINDEX('MRS.',FullName) = 1 THEN 'Mrs'
WHEN CHARINDEX('MISS ',FullName) = 1 THEN 'Miss'
WHEN CHARINDEX('MISS.',FullName) = 1 THEN 'Miss'
WHEN CHARINDEX('MASTER ',FullName) = 1 THEN 'Master'
WHEN CHARINDEX('MASTER.',FullName) = 1 THEN 'Master'
ELSE NULL
END AS [Title], FullName
FROM FullNameTest
) B
) C
Thanks so much for your help

Related

SQL-Server CONCAT case

I have the following exercise:
concatenate first, middle, last name and name suffix to form the
customer’s name in the following format: FirstName [MiddleName.]
LastName[, Suffix]. Note that NULL values should be omitted.
I interpret this as the following scenario (created the table from the image and inserted some values):
Please find my sample data below, name is #TB_Customer
The column CustomerName is the expected result and should be of form
FirstName MiddleName.LastName, Suffix if i have enteries for all
the fields.
MiddleName and Suffix can be optional, so the cases are:
If there is a suffix but not a MiddleName then CustomerName
should be of form Firstname LastName,Suffix
If there is a MiddleName but not a suffix then CustomerName
should be of form FirstName MiddleName.LastName
If both MiddleName and Suffix are null then CustomerName should
be of form FirstName LastName)
This is what i'm getting:
But as you can see the CustomerName case query I wrote doesn't work as expected (please see the cases above with bullets)
The query I wrote to get the CustomerName column is:
SELECT
(case
when (MiddleName is not null and Suffix is not null) then
CONCAT(c.FIRSTNAME,' ', c.MiddleName,'.', c.LASTNAME, ', ',Suffix)
when (MiddleName is null and suffix is null) then
CONCAT(c.FIRSTNAME,' ' ,c.LASTNAME)
when (MiddleName is null and Suffix is not null )then
concat (c.FirstName, ' ', c.LastName, ', ',Suffix )
when (Suffix is null and MiddleName is not null) then
concat (c.FirstName, ' ',MiddleName,'.',LastName)
end
)AS CustomerName
,c.*
FROM #TB_Customer c;
I have 2 questions:
Did I understand the exercise and do i have a good logic?
Where have I made a mistake and what's the correct query?
Using SQL-Server 2012
edit
to recreate my scenario please see the code below (sorry for not linking a fiddle but the website is not responding at my current location)
CREATE TABLE #TB_Customer
(
CustomerID int , --PK
Title varchar(50),
FirstName varchar(50),
MiddleName varchar(50),
LastName varchar(50),
Suffix varchar(50),
EmailAddress varchar(50),
Phone varchar(50),
Gender varchar(50),
Birthdate varchar(50),
--no fk
PRIMARY KEY (CustomerID)
)
insert into #TB_Customer values
('1','Sir','John','Jacob','Adams','St','johnJacobAdams#gmail.com','0677731235','M','1989-04-06'),
('2','Mr.','Russel','Thyrone','Peterson','pr','thyronePeterson#yahoo.com','555-010405','M','1963-02-01'),
('3','Ms.','Anne','Candice','Acola','aca','CandiceA#gmail.com','07408989989','F','1988-05-19'),
('4','Mrs.','Sophia','Veronika','Turner','tvs','SophiaVT#facebook.de','0423423887','F','1983-06-20'),
('5','Ms','Margaret','','Delafleur','','delaMarg#yahoo.co','233223355','Female','1982-02-25'),
('6','Mrs','Jessica','Luana','Cruz','','Jess#yahoo.com','787876631','Female','1922-05-05'),
('7','Mr','Dyrius','','Cruz','dc','dyr33#yahoo.com','0673332211','Male','1987-03-01')
update #TB_Customer
set Gender = 'Male' where Gender = 'M'
update #TB_Customer
set Gender = 'Female' where Gender = 'F'
Something like this should work as well...
SELECT concat(firstname
,CASE WHEN ISNULL(middlename,'') <> '' THEN ' '+middlename+'.'
WHEN ISNULL(middlename,'') <> '' AND ISNULL(suffix,'') = '' THEN '.'
ELSE ' ' END
,lastname
,CASE WHEN ISNULL(suffix,'') <> '' THEN ', '+suffix END)
FROM #TB_Customer
OUTPUT:
John Jacob.Adams, St
Russel Thyrone.Peterson, pr
Anne Candice.Acola, aca
Sophia Veronika.Turner, tvs
Margaret Delafleur
Jessica Luana.Cruz
Dyrius Cruz, dc
John Adams, St
I cant see the error in you query, i know if one of the columns is null, all the others will be, but try this way:
SELECT COALESCE(c.FIRSTNAME,'') + ' ' +
CASE WHEN COALESCE(c.MiddleName,'') = ''
THEN ''
ELSE c.MiddleName + '.'
END
+ COALESCE(c.LASTNAME,'') +
CASE WHEN COALESCE(Suffix,'') = ''
THEN ''
ELSE ', ' + Suffix
END AS CustomerName, c.*
FROM #TB_Customer c;
#Henrik is right, '' and NULL are diferent things
Now that you've given the code, I can see what the error is: The empty string is different from NULL.
So your tests for the presence of a middle name/suffix will always be true.
Either set those fields to NULL, or augment the test to check for NULL or empty strings.
SELECT
STUFF(RTRIM(
CONCAT(' ',
COALESCE(NULLIF(FirstName,'') + ' ', ''),
COALESCE(NULLIF(MiddleName,'') + '.', ''),
COALESCE(NULLIF(LastName,''), ''),
COALESCE(', ' + NULLIF(Suffix,'') , '')
)
), 2, 0,'')
FROM #TB_Customer tc
added the STUFF incase for some strange reason all you have is a Suffix

Simple select with trick

I need to write a select which will list all of the Clients however here is trick if client has status 1 or 2 it should mark this client with * before name. It should looks like
Vasya Pupkin
* Masha Pupkina
select looks like
select FirstName + ' '+ LastName, Address, DOB
from Clients
Order By FirstName
that means Masha is active client.
Spend almost 2 hours for searching in internet but cannot find anything useful. Because of that asking question here.
You need to rely on the use of the CASE structure to check the status field.
Check this fully functional code with sample data on SQL Fiddle
SELECT (CASE
WHEN status IN (1, 2) THEN '* '
ELSE ''
END) + FirstName + ' '+ LastName as Client_List
FROM Clients
ORDER BY FirstName
Data:
[id] [status] [FirstName] [LastName]
1 3 Vasya Pupkin
2 2 Masha Pupkina
3 3 Sasha Alexeivich
4 1 Katya Alexeivna
Result:
CLIENT_LIST
* Katya Alexeivna
* Masha Pupkina
Sasha Alexeivich
Vasya Pupkin
EDIT
Ouch! too late, muhmud answer is correct
select (case when status in (1, 2) then '* ' else '' end) + FirstName + ' '+ LastName, Address, DOB
from Clients
Order By FirstName

Teradata String Manipulation (Second Space)

I'm having great difficulty solving this seemingly easy task:
Purpose:
Create a query that eliminates the middle Initial
Example
Name
Smith, John A
Jane, Mary S
I would like an output such as this:
Name
Smith, John
Jane, Mary
Any tips on how to do this with Teradata SQL
I believe I solved the issue, albeit in a very poor way:
SELECT SUBSTR('SMITH, JOHN A', 0, (POSITION(' ' IN 'SMITH, JOHN A') + (POSITION(' ' IN SUBSTR('SMITH, JOHN A',(POSITION(' ' IN 'SMITH, JOHN A'))+ 1,50)))))
select a,
substr(a,1,index(a,' '))|| substr(trim(substr(a,index(a,' '))),1,index(trim(substr(a,index(a,' '))),' ')),
substr(trim(substr(a,index(a,' '))),index(trim(substr(a,index(a,' '))),' ')) last_name
from a
The challenge is making sure your names are consistently formatted. (Last_Name, Given_Name Middle_Initial) If they are then you may be able to solve this with recursive SQL. The following SQL would take Given_Name Last_Name and return Last_Name. You may be able to tweak it to accomplish your specific task. (My sample data was not consistently formatted so I was stuck trying to find the second (or third) occurrence of a white space character.)
WITH RECURSIVE cte (FullName, DelimPosition, RecursionLevel, Element, Remainder) AS
(
SELECT FullName
, 0 AS DelimPosition_
, 0
, CAST('' AS VARCHAR(128))
, FullName
FROM MyDatabase.Persons
UNION ALL
SELECT FullName
, CASE WHEN POSITION(' ' IN Remainder) > 0
THEN POSITION(' ' IN Remainder)
ELSE CHARACTER_LENGTH(Remainder)
END DelimPosition_
, RecursionLevel + 1
, SUBSTRING(Remainder FROM 0 FOR DelimPosition_ + 1)
, SUBSTRING(Remainder FROM DelimPosition_ + 1)
FROM cte
WHERE DelimPosition_ > 1
AND RecursionLevel < 3 -- Set max depth
)
SELECT FullName
, CASE WHEN POSITION('&' IN Element) = 0
THEN Element
ELSE NULL
END AS LastName
FROM cte c
WHERE RecursionLevel > 2
ORDER BY FullName;
Another option would be to implement a UDF that returns the rightmost n characters of a string. (e.g.RIGHT(FullName, n))
If the formatting is not consistent then we have to look at other less graceful options.
Hope this helps.

Trouble combining rows into one column using CAST(

Ok SO, here's your time to shine!
No really, I'm getting my butt kicked by an MS-SQL query that I can't seem to get to work.
What I am trying to do is search on a patient name; but also return patients who have a similar first or last name to the querying patient's last name. So "John Smith" can return anyone named "John Smith" or anyone who has a first or last name like "smith". If the a patient has multiple disease states, then combine those disease states into a single column. I have the following tables (though of course there are many more columns, but these are the most imortant):
Patient Table
PatientID FirstName LastName UserIDFK
10000 John Smith 1
10001 Miss Smith 2
10002 Smith Bomb 3
10003 Bobby Smith 4
-- etc
DiseaseStateForUser
UserIDFK DiseaseStateRefId
1 1
1 2
2 2
3 1
3 2
4 1
GlobalLookUp
RefId Ref_Code
1 HIV
2 HEPC
The results I'm looking for are this:
PatientID FirstName LastName DiseaseStates
10000 John Smith HIV|HEPC
10001 Miss Smith HEPC
10002 Smith Bomb HIV|HEPC
10003 Bobby Smith HIV
I've taken the examples from these questions (and countless others):
Is there a way to create a SQL Server function to “join” multiple
rows from a subquery into a single delimited
field?
Simulating group_concat MySQL function in MS SQL Server
2005?
As well as from this blog post Emulating MySQL’s GROUP_CONCAT() Function in SQL Server 2005 I came up with the following SQL procedure
DECLARE
#PatientID INT=null,
#FirstName Varchar(15)= null,
#LastName Varchar(15)= 'Smith',
#Name Varchar(15) = 'John Smith',
Select
Patient.First_Name,
Patient.Last_Name,
patient.PatientID,
(select CAST(GlobalLookUp.Ref_Code + '|' as VARCHAR(MAX))
from
TBL_PATIENT patient
,TBL_GBLLOOKUP GlobalLookUp
,TBL_DiseaseStateForUser DiseaseStateForUser
-- Try and make a collection of all the PatientIDs
-- that match the search criteria
-- so that only these are used to build
-- the DiseaseStatesColumn
,(Select
Patient.PatientID
FROM TBL_PATIENT patient
,TBL_SITEMASTER SiteMaster
,TBL_USERMASTER UserMaster
,TBL_USERSINSITES UserInSites
,TBL_GBLLOOKUP GlobalLookUp
,TBL_DiseaseStateForUser DiseaseStateForUser
WHERE (((patient.[Last_Name] like #LastName + '%') OR (patient.[Last_Name] Like #Name + '%' ))
OR ((patient.[First_Name] Like #Name + '%' ))
OR (patient.[First_Name] + ' ' + patient.[Last_Name] Like #Name + '%' ))
AND UserMaster.User_Id = UserInSites.User_Id_FK
AND UserInSites.Site_Id_FK = SiteMaster.Site_Id
AND UserInSites.Is_Active = 'True'
AND patient.[User_Id_FK] = UserMaster.[User_Id]
AND (DiseaseStateForUser.User_Id_FK = patient.User_Id_FK
AND DiseaseStateForUser.DiseaseState_RefId_FK = GlobalLookUp.Ref_Id)
and DiseaseStateForUser.Is_Active='True'
AND patient.[Is_Active] = 'TRUE'
group by Patient.PatientID) as PATIENTIDs
where patient.PatientID = PATIENTIDs.PatientID
AND (DiseaseStateForUser.User_Id_FK = patient.User_Id_FK
AND DiseaseStateForUser.DiseaseState_RefId_FK = GlobalLookUp.Ref_Id)
For XML PATH('')) as MultiDiseaseState
FROM TBL_PATIENT patient, TBL_SITEMASTER SiteMaster ,TBL_USERMASTER UserMaster,TBL_USERSINSITES UserInSites, TBL_GBLLOOKUP GlobalLookUp, TBL_DiseaseStateForUser DiseaseStateForUser
WHERE (((patient.[Last_Name] like #LastName + '%') OR (patient.[Last_Name] Like #Name + '%' ))
or ((patient.[First_Name] Like #Name + '%' ))
OR (patient.[First_Name] + ' ' + patient.[Last_Name] Like #Name + '%' ))
AND patient.PatientID = patient.PatientID
AND UserMaster.User_Id = UserInSites.User_Id_FK
AND UserInSites.Site_Id_FK = SiteMaster.Site_Id
AND UserInSites.Is_Active = 'True'
AND patient.[User_Id_FK] = UserMaster.[User_Id]
AND DiseaseStateForUser.User_Id_FK = patient.User_Id_FK
AND DiseaseStateForUser.DiseaseState_RefId_FK = GlobalLookUp.Ref_Id
and DiseaseStateForUser.Is_Active='True'
AND patient.[Is_Active] = 'TRUE'
group by PatientID, patient.First_Name, patient.Last_Name, GlobalLookUp.Ref_Code
order by PatientID
Unfortunately, this query nets me the following:
PatientID FirstName LastName MultiDiseaseState
10000 John Smith HIV|HEPC|HEPC|HIV|HEPC|HIV
10001 Miss Smith HIV|HEPC|HEPC|HIV|HEPC|HIV
10002 Smith Bomb HIV|HEPC|HEPC|HIV|HEPC|HIV
10003 Bobby Smith HIV|HEPC|HEPC|HIV|HEPC|HIV
In other words, the select CAST(GlobalLookUp.Ref_Code + '|' as VARCHAR(MAX)) call is building up the MultiDiseaseState column with all of the disease states for ALL of the selected patients.
I know there is something fundamentally wrong with the most inner SELECT statement, but I'm having a hard time figuring out what it is and how to write the query so that it builds only the disease states for a given patient.
Kind of a long post, but are there any suggestions people can make given the code snippets I've provided?
You should be able to use the Stuff function (I think it's only on SQL 2005 and higher) to make this work, I took your example data and wrote a demonstration off of that
SET NOCOUNT ON
CREATE TABLE #Patient
(
PatientID INT,
FirstName varchar(25),
LastName varchar(25),
UserIDFK INT
)
INSERT INTO #PATIENT SELECT 10000,'John','Smith',1
INSERT INTO #PATIENT SELECT 10001,'Miss','Smith',2
INSERT INTO #PATIENT SELECT 10002,'Smith','Bomb',3
INSERT INTO #PATIENT SELECT 10003,'Bobby','Smith',4
CREATE TABLE #DiseaseStateForUser
(
UserIDFK int,
DiseaseStateRefId int
)
INSERT INTO #DiseaseStateForUser SELECT 1,1
INSERT INTO #DiseaseStateForUser SELECT 1,2
INSERT INTO #DiseaseStateForUser SELECT 2,2
INSERT INTO #DiseaseStateForUser SELECT 3,1
INSERT INTO #DiseaseStateForUser SELECT 3,2
INSERT INTO #DiseaseStateForUser SELECT 4,1
CREATE TABLE #GlobalLookUp
(
RefId int,
Ref_Code varchar(10)
)
INSERT INTO #GlobalLookUp SELECT 1,'HIV'
INSERT INTO #GlobalLookUp SELECT 2,'HEPC'
SELECT
PatientID,
UserIDFK,
FirstName,
LastName,
STUFF(
(SELECT '|' + l.Ref_Code
FROM #DiseaseStateForUser u with (Nolock)
JOIN dbo.#GlobalLookUp l with (nolock)
ON u.DiseaseStateRefId = l.RefId
WHERE u.UserIDFK = p.UserIDFK FOR XML PATH('')
)
, 1, 1, '')
FROM #PATIENT p with (Nolock)
GROUP BY PatientID, FirstName, LastName, UserIDFK

How to order by last name on a full name column?

I have a SQL Table with a column called FullName which contains, for example, "John Smith".
How can order the data by the last name that appears in the FullName column?
For a long name like "Laurence John Fishburne", I would like to order the data by the word "Fishburne".
Thus, names are stored in the order
First Name
Middle Names
Last Name
I am using Microsoft SQL Server 2005.
I would do something like:
SELECT FullName
FROM TABLE
ORDER BY REVERSE(SUBSTRING(REVERSE(FullName), 0, CHARINDEX(' ', REVERSE(FullName))))
Instead of calculating what the last name is each time you want to run the query, you can have a computed column that persists the actual value into a column that can be used as you would any other column.
ALTER TABLE Customer
ADD LastName AS
RIGHT(FullName, CHARINDEX(' ', REVERSE(FullName)) - 1) PERSISTED
This Adds the column LastName to your Customer table, uses the function specified to calculate the value of the last name, and stores it onto the physical table. The keyword PERSISTED at the end is required for the value to be saved to the disk, else it will be computed each time a query is run.
Edit:
To deal with values with no spaces:
ALTER TABLE Customer
ADD LastName AS
case when CHARINDEX(' ', REVERSE(FullName)) > 0
then RIGHT(FullName, CHARINDEX(' ', REVERSE(FullName)) - 1)
else
FullName
end
PERSISTED
Though you can fiddle with this function as you will to determine what happens in this case. My point is to show that case statements can be used. You may also want to cast all output paths to the same type to avoid type mismatches.
try this, it uses the minimal number of functions to find the last space in the FullName string, which should help performance:
DECLARE #YourTable table (FullNamevarchar(30))
INSERT #YourTable VALUES ('Harry Smith');
INSERT #YourTable VALUES ('John Davis');
INSERT #YourTable VALUES ('Allision Thomas Williams');
SELECT
FullName
,RIGHT(FullName, CHARINDEX(' ', REVERSE(FullName)) - 1) AS SortBy
FROM #YourTable
ORDER BY RIGHT(FullName, CHARINDEX(' ', REVERSE(FullName)) - 1)
OUTPUT:
FullName SortBy
------------------------------ ------------------------------
John Davis Davis
Harry Smith Smith
Allision Thomas Williams Williams
(3 row(s) affected)
For the best performance and most accurate sort, you need to split out the name into Firstname, MiddleName, and LastName fields. Only the user entering the data can really understand what portion of FullName is the last name.
Query
SELECT stringrowname FROM tablename
ORDER BY SUBSTRING_INDEX((stringrowname)," ",-1);
It will return the strings order by last word.
create table foo(fullname varchar(100))
go
insert into foo values ('Harry Smith');
insert into foo values ('John Davis');
insert into foo values ('Allision Thomas Williams');
SELECT fullname
, REVERSE(left(REVERSE(fullname), charindex(' ',REVERSE(fullname))-1))
FROM foo
ORDER BY REVERSE(left(REVERSE(fullname), charindex(' ',REVERSE(fullname))-1))
Output:
fullname (No column name)
John Davis Davis
Harry Smith Smith
Allision Thomas Williams Williams
When in doubt, do it yourself:
Select
*
From
Users
Order By
LTrim(Reverse(Left(Reverse(FullName), CharIndex(' ', Reverse(FullName))))) Asc,
FullName Asc -- Fall-over for when there isn't a last name
This will do the trick:
create table #tmpTable
(
ID int identity(1,1) primary key,
FullName varchar(100) not null
);
insert into #tmpTable(FullName) values('Big John Sansom');
insert into #tmpTable(FullName) values('Mike Douglas Reid');
insert into #tmpTable(FullName) values('First Second Last');
insert into #tmpTable(FullName) values('JustOneTokenForName');
select
FullName,
LastName = case
when CHARINDEX(FullName,' ') = 0 THEN FullName
else RIGHT(FullName, CHARINDEX(' ', REVERSE(FullName)) - 1)
end
from #tmpTable
order by LastName
drop table #tmpTable
It really depends on how names are stored. Assuming "Last, First Middle" you could do something like
order by substring(0, charindex(',', FullName))
You may have to fiddle with it a little, but I believe that should work.