T-SQL substring - separating first and last name - sql-server-2008

I have a column which has FirstName and LastName together. I'm writing a report to separate the FirstName And LastName. How do I get the FirstName and LastName separated in T-SQL?

Assuming the FirstName is all of the characters up to the first space:
SELECT
SUBSTRING(username, 1, CHARINDEX(' ', username) - 1) AS FirstName,
SUBSTRING(username, CHARINDEX(' ', username) + 1, LEN(username)) AS LastName
FROM
whereever

The easiest way I can find to do it is:
SELECT
SUBSTRING(FullName, 1, CHARINDEX(' ', FullName) - 1) AS FirstName,
REVERSE(SUBSTRING(REVERSE(FullName), 1, CHARINDEX(' ', REVERSE(FullName)) - 1)) AS LastName
FROM
[PERSON_TABLE]

This should work:
Select
LTRIM(RTRIM(SUBSTRING(FullName, 0, CHARINDEX(' ', FullName)))) As FirstName
, LTRIM(RTRIM(SUBSTRING(FullName, CHARINDEX(' ', FullName)+1, 8000)))As LastName
FROM TABLE
Edit: Adopted Aaron's and Jonny's hint with the fixed length of 8000 to avoid unnecessary calculations.

validate last name is blank
SELECT
person.fullName,
(CASE WHEN 0 = CHARINDEX(' ', person.fullName)
then person.fullName
ELSE SUBSTRING(person.fullName, 1, CHARINDEX(' ', person.fullName)) end) as first_name,
(CASE WHEN 0 = CHARINDEX(' ', person.fullName)
THEN ''
ELSE SUBSTRING(person.fullName,CHARINDEX(' ', person.fullName), LEN(person.fullName) )end) last_name
FROM person

Here is a more elaborated solution with a SQL function:
GetFirstname
CREATE FUNCTION [dbo].[ufn_GetFirstName]
(
#FullName varchar(500)
)
RETURNS varchar(500)
AS
BEGIN
-- Declare the return variable here
DECLARE #RetName varchar(500)
SET #FullName = replace( replace( replace( replace( #FullName, '.', '' ), 'Mrs', '' ), 'Ms', '' ), 'Mr', '' )
SELECT
#RetName =
CASE WHEN charindex( ' ', ltrim( rtrim( #FullName ) ) ) > 0 THEN left( ltrim( rtrim( #FullName ) ), charindex( ' ', ltrim( rtrim( #FullName ) ) ) - 1 ) ELSE '' END
RETURN #RetName
END
GetLastName
CREATE FUNCTION [dbo].[ufn_GetLastName]
(
#FullName varchar(500)
)
RETURNS varchar(500)
AS
BEGIN
DECLARE #RetName varchar(500)
IF(right(ltrim(rtrim(#FullName)), 2) <> ' I')
BEGIN
set #RetName = left(
CASE WHEN
charindex( ' ', reverse( ltrim( rtrim(
replace( replace( replace( replace( replace( replace( #FullName, ' Jr', '' ), ' III', '' ), ' II', '' ), ' Jr.', '' ), ' Sr', ''), 'Sr.', '')
) ) ) ) > 0
THEN
right( ltrim( rtrim(
replace( replace( replace( replace( replace( replace( #FullName, ' Jr', '' ), ' III', '' ), ' II', '' ), ' Jr.', '' ), ' Sr', ''), 'Sr.', '')
) ) , charindex( ' ', reverse( ltrim( rtrim(
replace( replace( replace( replace( replace( replace( #FullName, ' Jr', '' ), ' III', '' ), ' II', '' ), ' Jr.', '' ), ' Sr', ''), 'Sr.', '')
) ) ) ) - 1 )
ELSE '' END
, 25 )
END
ELSE
BEGIN
SET #RetName = left(
CASE WHEN
charindex( ' ', reverse( ltrim( rtrim(
replace( replace( replace( replace( replace( replace( replace( #FullName, ' Jr', '' ), ' III', '' ), ' II', '' ), ' I', '' ), ' Jr.', '' ), ' Sr', ''), 'Sr.', '')
) ) ) ) > 0
THEN
right( ltrim( rtrim(
replace( replace( replace( replace( replace( replace( replace( #FullName, ' Jr', '' ), ' III', '' ), ' II', '' ), ' I', '' ), ' Jr.', '' ), ' Sr', ''), 'Sr.', '')
) ) , charindex( ' ', reverse( ltrim( rtrim(
replace( replace( replace( replace( replace( replace( replace( #FullName, ' Jr', '' ), ' III', '' ), ' II', '' ), ' I', '' ), ' Jr.', '' ), ' Sr', ''), 'Sr.', '')
) ) ) ) - 1 )
ELSE '' END
, 25 )
END
RETURN #RetName
END
USE:
SELECT dbo.ufn_GetFirstName(Fullname) as FirstName, dbo.ufn_GetLastName(Fullname) as LastName FROM #Names

You could do this if firstname and surname are separated by space:
SELECT SUBSTRING(FirstAndSurnameCol, 0, CHARINDEX(' ', FirstAndSurnameCol)) Firstname,
SUBSTRING(FirstAndSurnameCol, CHARINDEX(' ', FirstAndSurnameCol)+1, LEN(FirstAndSurnameCol)) Surname FROM ...

This will take care of names like "Firstname Z. Lastname" and "First Z Last"
SELECT
CASE
WHEN CHARINDEX(' ',name) = 0 THEN name
WHEN CHARINDEX(' ',name) = PATINDEX('% _[., ]%',name) THEN RTRIM(SUBSTRING(name, 1, CHARINDEX(' ',name) + 2))
ELSE SUBSTRING(name,1, CHARINDEX(' ',name))
END [firstname]
,CASE
WHEN CHARINDEX(' ',name) = 0 THEN ''
WHEN CHARINDEX(' ',name) = PATINDEX('% _[., ]%',name) THEN LTRIM(SUBSTRING(name, CHARINDEX(' ',name) + 3,1000))
ELSE SUBSTRING(name,CHARINDEX(' ',name)+1,1000)
END [lastname]
FROM [myTable]

I think below query will be helpful to split FirstName and LastName from FullName even if there is only FirstName.
For example: 'Philip John' can be split into Philip and John.
But if there is only Philip, because of the charIndex of Space is 0, it will only give you ''.
Try the below one.
declare #FullName varchar(100)='Philp John'
Select
LTRIM(RTRIM(SUBSTRING(#FullName, 0, CHARINDEX(' ', #FullName+' ')))) As FirstName
, LTRIM(RTRIM(SUBSTRING(#FullName, CHARINDEX(' ', #FullName+' ')+1, 8000)))As LastName
Hope this will help you. :)

You may have problems if the Fullname doesn't contain a space.
Assuming the whole of FullName goes to Surname if there is no space and FirstName becomes an empty string, then you can use this:
SELECT
RTRIM(LEFT(FullName, CHARINDEX(' ', FullName))) AS FirstName,
SUBSTRING(FullName, CHARINDEX(' ', FullName) + 1, 8000) AS LastName
FROM
MyNameTable;

The code below works with Last, First M name strings. Substitute "Name" with your name string column name. Since you have a period as a final character when there is a middle initial, you would replace the 2's with 3's in each of the lines (2, 6, and 8)- and change "RIGHT(Name, 1)" to "RIGHT(Name, 2)" in line 8.
SELECT SUBSTRING(Name, 1, CHARINDEX(',', Name) - 1) LastName ,
CASE WHEN LEFT(RIGHT(Name, 2), 1) <> ' '
THEN LTRIM(SUBSTRING(Name, CHARINDEX(',', Name) + 1, 99))
ELSE LEFT(LTRIM(SUBSTRING(Name, CHARINDEX(',', Name) + 1, 99)),
LEN(LTRIM(SUBSTRING(Name, CHARINDEX(',', Name) + 1, 99)))
- 2)
END FirstName ,
CASE WHEN LEFT(RIGHT(Name, 2), 1) = ' ' THEN RIGHT(Name, 1)
ELSE NULL
END MiddleName

For the last name as in US standards (i.e., last word in the [Full Name] column) and considering first name to include a possible middle initial, middle name, etc.:
SELECT DISTINCT
[Full Name]
,REVERSE([Full Name]) -- to visualize what the formula is doing
,CHARINDEX(' ', REVERSE([Full Name])) -- finds the last space in the string
,[Last Name] = REVERSE(RTRIM(LTRIM(LEFT(REVERSE([Full Name]), CHARINDEX(' ', REVERSE([Full Name]))))))
,[First Name] = RTRIM(LTRIM(LEFT([Full Name], LEN([Full Name]) - CHARINDEX(' ', REVERSE([Full Name])))))
FROM ...
Note that this assumes [Full Name] has no spaces before or after the actual string. Otherwise, use RTRIM and LTRIM to remove these.

Let's suppose your table has Name column and it contains data like -
Random Person
FIRST LAST
Alpha Beta
The query will be like this
SELECT
LEFT(NAME,CHARINDEX(' ',NAME)) AS "First Name",
LTRIM(RTRIM(SUBSTRING(NAME,CHARINDEX(' ',NAME),100))) AS "Last Name"
FROM YOUR_TABLE

Related

Add additional column to query based on dates in another table

I'm looking to query the datatbl table and return the table with an additional column based on the Start_date values of the productstbl table.
Below I have created a schema with the initial table (datatbl_before) and the desired end result (datatbl_after), where a Product_Lookup column has been added to the datatbl table and shows the corresponding Product from the productstbl based on the Asset_ID of the line and the Start_date.
For example, in the first row of datatbl, the row relates to Asset_ID '1' and occurred on 2017-08-16, this would retrieve the product value 'A' from the productstbl table as it is after the start_date value 2017-08-12, but before the next start_date value of 2017-09-27 related to that Asset_ID .
In the case where there is no proceeding date for the Asset_ID lookup and start_date, a 'NONE' value could be returned - e.g. Row 5 of datatbl
Can anyone provide any direction or advice on how to tackle this one?
Many thanks in advance!
CREATE TABLE datatbl_before (
PDate DATE,
Asset_ID varchar(255),
Rev int
);
CREATE TABLE datatbl_after (
PDate DATE,
Asset_ID varchar(255),
Rev int,
Product_Lookup varchar(255)
);
CREATE TABLE productstbl (
Asset_ID varchar(255),
Start_date DATE,
Product varchar(255)
);
INSERT INTO productstbl
VALUES
(' 1 ',' 2017-08-12' ,' A' ),
(' 1' ,' 2017-09-27' ,' B' ),
(' 1' ,' 2018-02-14' ,' C' ),
(' 2' ,' 2018-01-17' ,' A' ),
(' 2' ,' 2018-02-21' ,' B' ),
(' 2' ,' 2018-03-05' ,' C' );
INSERT INTO datatbl_before
VALUES
(' 2017-08-16' ,' 1' ,' 10'),
(' 2017-09-29' ,' 1' ,' 15'),
(' 2018-02-13' ,' 1' ,' 12'),
(' 2018-02-19' ,' 1' ,' 10'),
(' 2018-01-01' ,' 2' ,' 12'),
(' 2018-01-25' ,' 2' ,' 33'),
(' 2018-02-25' ,' 2' ,' 67'),
(' 2018-03-07' ,' 2' ,' 71');
INSERT INTO datatbl_after
VALUES
(' 2017-08-16' ,' 1' ,' 10' ,' A' ),
(' 2017-09-29' ,' 1' ,' 15' ,' B' ),
(' 2018-02-13' ,' 1' ,' 12' ,' B' ),
(' 2018-02-19' ,' 1' ,' 10' ,' C' ),
(' 2018-01-01' ,' 2' ,' 12' ,' NONE'),
(' 2018-01-25' ,' 2' ,' 33' ,' A' ),
(' 2018-02-25' ,' 2' ,' 67' ,' B' ),
(' 2018-03-07' ,' 2' ,' 71' ,' C' );
You can use a sub query, which goes and selects the data you want, using data from the outer query in the where clause. In this case, it finds all the products that are the right asset, and started before or on the transaction date, sorts them so the most recent one is first (the one we want) and takes only that one (The TOP 1 part).
The ISNULL part changes any missing values from NULL to 'NONE'
Select DB.*,
ISNULL(
(Select top 1 product
from productstbl
where Asset_ID = DB.Asset_ID
and Start_date <= DB.PDate
order by Start_date desc
)
, 'NONE') as Product_Lookup
from datatbl_before DB;

Update statement in nested query

I have an update query.But it showing sql error
Query
Update ms_student set student_first_name=y.student_first_name,student_middle_name=y.student_middle_name,student_last_name=y.student_last_name FROM
(SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(student_name, ' ', 1), ' ', -1) as student_first_name,
SUBSTRING_INDEX(SUBSTRING_INDEX(student_name, ' ', 2), ' ', -1) as student_middle_name,
SUBSTRING_INDEX(SUBSTRING_INDEX(student_name, ' ', 3), ' ', -1) as student_last_name
FROM ms_student) y
My inner query working perfectly.But In update having issue
Why you need subquery you can do with simple update command
update ms_student
set
student_first_name= SUBSTRING_INDEX(SUBSTRING_INDEX(student_name, ' ', 1), ' ', -1),
student_middle_name = SUBSTRING_INDEX(SUBSTRING_INDEX(student_name, ' ', 2), ' ', -1),
student_last_name = SUBSTRING_INDEX(SUBSTRING_INDEX(student_name, ' ', 3), ' ', -1) ;
You came have multiple table in update clause
Update ms_student x,(SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(student_name, ' ', 1), ' ', -1) as student_first_name,
SUBSTRING_INDEX(SUBSTRING_INDEX(student_name, ' ', 2), ' ', -1) as student_middle_name,
SUBSTRING_INDEX(SUBSTRING_INDEX(student_name, ' ', 3), ' ', -1) as student_last_name
FROM ms_student) y
set x.student_first_name=y.student_first_name,x.student_middle_name=y.student_middle_name,x.student_last_name=y.student_last_name;

Select string till first or second space in string

I have table column that contains in each row data like this:
| Simbols |
|--------------------------------------|
|H412 Text text |
|H413 Text text text text |
|EUH 001 Text text text text text text |
|EUH 006 text text |
|EUH 201/201A Text text. Text text |
And I need from that data get data like this:
|Simbols |
|------------|
|H412 |
|H413 |
|EUH 001 |
|EUH 006 |
|EUH 201/201A|
I tried with SUBSTRING and CHARINDEX but it till the end don't work... It takes only first space or something like that...
QUERY:
SELECT
CASE
WHEN SUBSTRING(Simbols, 1, CHARINDEX(' ', Simbols)) = ''
THEN Simbols + ' '
ELSE SUBSTRING(Simbols, 1, CHARINDEX(' ', Simbols))
END 'Simbols'
FROM dbo.table
RESULT:
| Simbols |
|------------|
|H412 |
|H413 |
|EUH |
|EUH |
|EUH |
How can I make this work, and where is the problem?
Maybe there is different way to get these Simbols?
P.S. "Text text text" is a example, there comes a explanations of "Simbols"
The CharIndex() function has an optional 3rd parameter - start_location - that will be key here.
SELECT your_column
, CharIndex(' ', your_column) As first_space
, CharIndex(' ', your_column, CharIndex(' ', your_column) + 1) As second_space
, SubString(your_column, 1, CharIndex(' ', your_column, CharIndex(' ', your_column) + 1)) As first_two_words
FROM your_table
Unfortunately when the CharIndex() function can't find the specified string (in this case a single space ' ') it will return 0 (zero).
This means that if there isn't a first or second space the result of first_two_words in my above example will return an empty string as SubString(your_column, 1, 0) = ''.
To get around this you need to be a little clever.
Essentially, if second_space = 0 then we need to return the full string. We have a few options for this:
SELECT your_column
, CharIndex(' ', your_column) As first_space
, CharIndex(' ', your_column, CharIndex(' ', your_column) + 1) As second_space
, SubString(your_column, 1, CharIndex(' ', your_column, CharIndex(' ', your_column) + 1)) As first_two_words
, SubString(your_column, 1, Coalesce(NullIf(CharIndex(' ', your_column, CharIndex(' ', your_column) + 1), 0), Len(your_column))) As first_two_words_option1
, CASE WHEN CharIndex(' ', your_column, CharIndex(' ', your_column) + 1) = 0 THEN your_column ELSE SubString(your_column, 1, CharIndex(' ', your_column, CharIndex(' ', your_column) + 1)) END As first_two_words_option2
FROM (
SELECT 'one' As your_column
UNION ALL SELECT 'one two'
UNION ALL SELECT 'one two three'
UNION ALL SELECT 'one two three four'
) As x
Try this: It works
SELECT CASE WHEN charindex(' ', Simbols, charindex(' ', Simbols) + 1) = 0
THEN Simbols
ELSE LEFT(Simbols, charindex(' ', Simbols, charindex(' ', Simbols) + 1))
END
FROM dbo.table
Here is screenshot what I tried.
Here is new EDIT
SELECT REPLACE(Simbols, 'text', '') FROM dbo.table
Here is screen shot
Try something like this:
select TRIM(REPLACE(lower(type),"text" ,"")) as T, type from supportContacts
Sql Fiddle: http://sqlfiddle.com/#!2/d5cf8/4
for more info :http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_replace

SQL / Coalesce - Wrong row name

I have a problem with the request below!
REQUEST:
SELECT COALESCE(date(date_field), 'Total') AS "date_field_group",
COUNT( id_field ) AS "Nombre de bookings",
CONCAT( REPLACE( REPLACE( FORMAT( SUM( price1 ) , 2 ) , ',', ' ' ) , '.', ',' ) , ' €' ) AS "Total à l'achat",
CONCAT( REPLACE( REPLACE( FORMAT( SUM( price2 ) , 2 ) , ',', ' ' ) , '.', ',' ) , ' €' ) AS "Total à la vente",
CONCAT( REPLACE( REPLACE( FORMAT( SUM( price2 ) - SUM( price1 ) , 2 ) , ',', ' ' ) , '.', ',' ) , ' €' ) AS 'Marge',
CONCAT( REPLACE( FORMAT( (SUM( price2 ) / SUM( price1 ) ) , 2 ) , '1.', '' ) , ' ', '%') AS "Markup moyen"
FROM table1 S, table2 B
WHERE status_field
IN ( "1", "5")
AND DATE( date_field ) BETWEEN "2011-08-01" AND "2011-08-31"
AND type_field = "H"
AND price1 IS NOT NULL
AND S.id_field = B.id_field
AND B.id2 = "1"
GROUP BY date_field_group WITH ROLLUP
The thing is that the request is working fine (right numbers), but in the last line I was expected to get in first row "Total" and instead of that I got a field NULL...
Is someone know what is wrong with my request ?
Thx for any help ;).
You're query is almost correct (except for using implicit SQL '89 joins, which is an SQL anti-pattern)
The problem is in the last line: GROUP BY ... WITH ROLLUP.
The rollup gets applied very late in the process, after your COALESCE(date(date_field), 'Total').
So the coalesce has already finished by the time rollup comes along you need to rewrite the query like so:
SELECT COALESCE(date_field_group, 'Total') as date_field_group
, `Nombre de bookings`
, `Total à l'achat`
, `Total à la vente`
, `Marge`
, `Markup moyen`
FROM (
SELECT date(date_field) AS "date_field_group",
COUNT( id_field ) AS "Nombre de bookings",
CONCAT( REPLACE( REPLACE( FORMAT( SUM( price1 ) , 2 ) , ',', ' ' ) , '.', ',' ) , ' €' ) AS "Total à l'achat",
CONCAT( REPLACE( REPLACE( FORMAT( SUM( price2 ) , 2 ) , ',', ' ' ) , '.', ',' ) , ' €' ) AS "Total à la vente",
CONCAT( REPLACE( REPLACE( FORMAT( SUM( price2 ) - SUM( price1 ) , 2 ) , ',', ' ' ) , '.', ',' ) , ' €' ) AS 'Marge',
CONCAT( REPLACE( FORMAT( (SUM( price2 ) / SUM( price1 ) ) , 2 ) , '1.', '' ) , ' ', '%') AS "Markup moyen"
FROM table1 S
INNER JOIN table2 B ON (S.id_field = B.id_field)
WHERE status_field IN ( "1", "5")
AND DATE( date_field ) BETWEEN "2011-08-01" AND "2011-08-31"
AND type_field = "H"
AND price1 IS NOT NULL
AND B.id2 = "1"
GROUP BY date_field_group WITH ROLLUP ) AS subquery

How to return a product and all of its attributes from another table whilst filtering by one of them

i have gotten our system to return information correctly, however i now have the problem of returning information when we are applying filters. the query in question is:
SELECT `products`.*
CONCAT( '[', GROUP_CONCAT( DISTINCT '{', '"id":"', `product_variations`.`id`, '"', ',', '"id_product":"', `product_variations`.`id_product`, '"', ',', '"sku":"', `product_variations`.`sku`, '"', ',', '"options":"', `product_variations`.`options`, '"', ',', '"quantity":"', `product_variations`.`quantity`, '"', ',', '"price":"', `product_variations`.`price`, '"', '}' SEPARATOR ',' ), ']' ) as `_variations`,
CONCAT( '[', GROUP_CONCAT( DISTINCT '{', '"id":"', `product_images`.`id`, '"', ',', '"id_product":"', `product_images`.`id_product`, '"', ',', '"location":"', `product_images`.`location`, '"', ',', '"order":"', `product_images`.`order`, '"', ',', '"variation_key":"', `product_images`.`variation_key`, '"', ',', '"variation_values":"', `product_images`.`variation_values`, '"', '}' SEPARATOR ',' ), ']' ) as `_images`,
CONCAT( '[', GROUP_CONCAT( DISTINCT '{', '"id":"', `product_attributes`.`id`, '"', ',', '"id_product":"', `product_attributes`.`id_product`, '"', ',', '"key":"', `product_attributes`.`key`, '"', ',', '"value":"', `product_attributes`.`value`, '"', '}' SEPARATOR ',' ), ']' ) as `_attributes`
FROM (`products`)
LEFT JOIN product_variations ON products.id = product_variations.id_product
LEFT JOIN product_images ON products.id = product_images.id_product
LEFT JOIN product_attributes ON products.id = product_attributes.id_product
WHERE ( `products`.`id_category` = "11" OR `products`.`id_category` = "12" OR `products`.`id_category` = "9" )
AND ( product_attributes.key = "color" AND product_attributes.value IN ( "Red","Orange" ) )
AND ( product_attributes.key = "size" AND product_attributes.value IN ( "L","M" ) )
GROUP BY `products`.`id`
LIMIT 10
Ignoring the CONCAT things ( which return the attributes etc as json data ) what i am trying to get to work is, for example, if a product has the following attributes:
color=Red
color=Orange
color=Blue
size=L
size=M
size=S
so if i want to display products where
color=Red OR color=Orange
AND
size=L OR size=M
this product would be returned,
the problem obviusly lies in the fact
AND ( product_attributes.key = "color"
AND ( product_attributes.key = "size"
cannot be true, but i want it to be ANY of the children, if it is only 1 filter, it works fine, but multiple "different" filters and it breaks,
also, it will only return the attributes in question which passed the filter.
i would like it to return ALL attributes for a product which has passed the filter, the more i look into this the more i think its not possible. Does anybody know of a way of doing this?
You would need to write the logic in a HAVING clause, example:
HAVING COUNT(IF(product_attributes.key = "color" AND product_attributes.value IN ("Red","Orange"),
1, NULL)) > 0
AND COUNT(IF( product_attributes.key = "size" AND product_attributes.value IN ( "L","M" ),
1, NULL)) > 0