SQL Server Substring - sql

When the following query is executed if fails to run unless the 2nd substring statement (commented out here) is uncommmented. What is going on here that I am missing?
Uses the Northwind database
SELECT Substring(Contactname, Charindex(' ', Contactname) + 1, Len(Contactname))AS LastName,
Substring(Contactname, 1, Charindex(' ', Contactname) - 1) AS FirstName1
--, substring(ContactName, 1, 4) AS FirstName2
-- if this line is commented out then the query crashes with the error msg
--Invalid length parameter passed to the LEFT or SUBSTRING function.
,
Phone,
Orderid,
Orderdate
FROM customers
INNER JOIN orders
ON customers.Customerid = orders.Customerid

Charindex(' ', Contactname) - 1
Returns -1 if Contactname does not contain a space. This is an invalid length parameter.
There must be a Contactname that causes the Substring expression to fail but that is filtered out by the JOIN.
Presumably the compute scalar shifts around between the two plans and happens to be evaluated after the join when you have that line uncommented.
See SQL Server should not raise illogical errors for some discussion on this type of issue.
A way around this would be to append a space to the input to Charindex
Substring(Contactname, 1, Charindex(' ', Contactname + ' ' ) - 1)

You need to watch out for the negative cases. A null value, a empty string, or a one name person.
I used a Common Table Expression since I did not want the charindex() function all over the place.
Also, your first substring() did not substract the correct amount of characters.
-- Use the sample db
use [Northwind]
go
-- Watch out for null & one name
;
with cteContactsOrders
as
(
SELECT
Contactname as FullName,
Substring(IsNull(Contactname, ''), 1, 4) as FirstFour,
Charindex(' ', IsNull(Contactname, '')) as Pos,
Phone,
Orderid,
Orderdate
FROM
customers as c
INNER JOIN
orders as o
ON
c.Customerid = o.Customerid
)
select
co.*,
case
when Pos > 0 then substring(FullName, 1, Pos-1)
when Pos = 0 and len(ltrim(rtrim(FullName))) > 0 then FullName
else ''
end as FirstName,
case
when Pos > 0 then substring(FullName, Pos+1, len(FullName) - Pos)
else ''
end as LastName
from
cteContactsOrders co
The output on SQL Server 2014 CTP2.

Related

How to find second value inside a column

How do I list the names of all band members with the same last name?
The column has values like this
band_NAME
-------------------
Carla Thomas
Stephen E. Rice
Cynthia P. Tree
Richard Anthony Paul
Ann Frances Smith
Lorace Black
Timothy Adam Paul
I know we would have to use instr and substr. I just don't get how we would determine the position.
I know the basic format is going to be like
SELECT band_NAME
FROM TABLE
where substr(band_name, ?, instr( ) IN
(select substr(band_name, ?, instr( )-1)
from table
group by SUBSTR(band_NAME , ?, INSTR( )-1 )
HAVING COUNT(* ) > 1 );
But what goes in the question marks and inside the instr?
Would appreciate any help on this!
I'm assuming that your delimiter between first and last name is a single space. More spaces in the string are a part of last name. Thus, you probably want to search for the first space character.
Return position of the first occurence of substring with instr(str, substr).
Then, use substring(str, pos) to return the substring starting at a given position (feed by instr function).
SELECT substring(band_name, instr(band_name, ' '))
FROM yourtable
Try this:
SELECT t1.band_NAME
FROM TABLE t1 LEFT JOIN TABLE t2
ON SUBSTRING_INDEX(t1.band_name, ' ', - 1) = SUBSTRING_INDEX(t2.band_name, ' ', - 1)
WHERE t1.band_name <> t2.band_name
And this like your pseudocode MySQL:
SELECT band_NAME FROM TABLE
Where FIND_IN_SET (SUBSTRING_INDEX(band_name, ' ', -1),
(Select SUBSTRING_INDEX(band_name, ' ', -1) bn
From TABLE Group by bn
having Count(bn) > 1
)
)
SQL Server
SELECT band_NAME FROM TABLE
Where
SUBSTRING(band_NAME, CHARINDEX(' ', band_NAME) + 1, LEN(band_NAME)) AS [Last Name]
IN
(Select SUBSTRING(band_NAME, CHARINDEX(' ', band_NAME) + 1, LEN(band_NAME)) AS [Last Name]
From TABLE Group by [Last Name] -- or SUBSTRING(band_NAME, CHARINDEX(' ', band_NAME) + 1, LEN(band_NAME)) AS [Last Name]
having Count(*) > 1
)
)
Additionally I thinks you can benefit from STRING_SPLIT in some way
Try This
with cte as
(
select band_name, ROW_NUMBER() over(partition by SUBSTRING(band_name,CHARINDEX(' ',band_name),LEN(band_name)) order by band_name) as cnt,
SUBSTRING(band_name,CHARINDEX(' ',band_name),LEN(band_name)) as lastname
from your_table
)
select band_name
from cte
where lastname in (select lastname from cte where cnt > 1)
okay,
The best solution is to change your schema and store last name in a separate column.
In the mean time you could get the last name like this,
SELECT
[band_NAME],
CASE WHEN CHARINDEX(' ', [band_NAME]) > 0
THEN
RIGHT([band_NAME], CHARINDEX(' ', REVERSE([band_NAME])))
ELSE
[band_NAME]
END [LastName]
FROM
[TABLE]
You could then group them like this
SELECT
[LastName],
COUNT(*)
FROM
(
SELECT
[band_NAME],
CASE WHEN CHARINDEX(' ', [band_NAME]) > 0
THEN
RIGHT([band_NAME], CHARINDEX(' ', REVERSE([band_NAME])))
ELSE
[band_NAME]
END [LastName]
FROM
[TABLE]
) [TABLEWithLastName]
GROUP BY
[LastName];

Removing spaces in complex names

I am trying to insert complex names like Juan Carlos but I want to remove all the spaces except the one between Juan and Carlos. Lets use # as space to see spaces better.
When inserting I have tried RTRIM(LTRIM(#Name)) however It seems not to work, I tried to insert ###Jua#Car### but when I select the field with DATALENGTH([Name]) I get the lenght of 14.
As I see that string I can count 13 characters, not 14.
1. What is the character I cannot count?
2. How can I end up getting Juan#Carlos removing all the spaces if LTRIM and RTRIM does not work?
Update with more info:
The column datatype is nvarchar(100)
I just tried REPLACE([Name], ' ','') and the lenght i get is 12
You can trim non-alphanumeric characters using a somewhat complicated method:
select t2.name2
from t outer apply
(select (case when name like '%[a-zA-Z0-9]%'
then stuff(t.name, 1, patindex(t.name, '%[a-zA-Z0-9]%'), '')
else ''
end) as name1
) t1 outer apply
(select (case when name1 like '%[a-zA-Z0-9]%'
then left(t1.name1,
len(t1.name1) - patindex(reverse(t.name), '%[a-zA-Z0-9.]%')
)
else ''
end) as name2
) t2
Try to use in following, select separately FirstName, LastName and concat them with space:
DECLARE #FullName VARCHAR(MAX) = ' Juan Carlos '
SELECT LEN(SUBSTRING(LTRIM(RTRIM(#FullName)), 1, CHARINDEX(' ', LTRIM(RTRIM(#FullName))) - 1) + ' ' +
REVERSE(SUBSTRING(REVERSE(LTRIM(RTRIM(#FullName))), 1,
CHARINDEX(' ', REVERSE(LTRIM(RTRIM(#FullName)))) - 1) )) AS [Len]
It returning len = 11

How to get middle portion from Sql server table data?

I am trying to get First name from employee table, in employee table full_name is like this: Dow, Mike P.
I tried with to get first name using below syntax but it comes with Middle initial - how to remove middle initial from first name if any. because not all name contain middle initial value.
-- query--
select Employee_First_Name as full_name,
SUBSTRING(
Employee_First_Name,
CHARINDEX(',', Employee_First_Name) + 1,
len(Employee_First_Name)) AS FirstName
---> remove middle initial from right side from employee
-- result
Full_name Firstname Dow,Mike P. Mike P.
--few example for Full_name data---
smith,joe j. --->joe (need result as)
smith,alan ---->alan (need result as)
Instead of specifying the len you need to use charindex again, but specify that you want the second occurrence of a space.
select Employee_First_Name as full_name,
SUBSTRING(
Employee_First_Name,
CHARINDEX(',', Employee_First_Name) + 1,
CHARINDEX(' ', Employee_First_Name, 2)) AS FirstName
One thing to note, the second charindex can return 0 if there is no second occurence. In that case, you would want to use something like the following:
select Employee_First_Name as full_name,
SUBSTRING(
Employee_First_Name,
CHARINDEX(',', Employee_First_Name) + 1,
IIF(CHARINDEX(' ', Employee_First_Name, 2) = 0, Len(Employee_First_name), CHARINDEX(' ', Employee_First_Name, 2))) AS FirstName
This removes the portion before the comma.. then uses that string and removes everything after space.
WITH cte AS (
SELECT *
FROM (VALUES('smith,joe j.'),('smith,alan'),('joe smith')) t(fullname)
)
SELECT
SUBSTRING(
LTRIM(SUBSTRING(fullname,CHARINDEX(',',fullname) + 1,LEN(fullname))),
0,
COALESCE(NULLIF(CHARINDEX(' ',LTRIM(SUBSTRING(fullname,CHARINDEX(',',fullname) + 1,LEN(fullname)))),0),LEN(fullname)))
FROM cte
output
------
joe
alan
joe
To be honest, this is most easily expressed using multiple levels of logic. One way is using outer apply:
select ttt.firstname
from t outer apply
(select substring(t.full_name, charindex(', ', t.full_name) + 2, len(t.full_name) as firstmi
) tt outer apply
(select (case when tt.firstmi like '% %'
then left(tt.firstmi, charindex(' ', tt.firstmi)
else tt.firstmi
end) as firstname
) as ttt
If you want to put this all in one complicated statement, I would suggest a computed column:
alter table t
add firstname as (stuff((case when full_name like '%, % %.',
then left(full_name,
charindex(' ', full_name, charindex(', ', full_name) + 2)
)
else full_name
end),
1,
charindex(', ', full_name) + 2,
'')
If format of this full_name field is the same for all rows, you may utilize power of SQL FTS word breaker for this task:
SELECT N'Dow, Mike P.' AS full_name INTO #t
SELECT display_term FROM #t
CROSS APPLY sys.dm_fts_parser(N'"' + full_name + N'"', 1033, NULL, 1) p
WHERE occurrence = 2
DROP TABLE #t

Extract last name, first name and suffix into separate columns

I was wondering if someone could provide me an easy way to extract the names into different columns as below. There is a comma after the Last Name and space between First Name, Middle Initial, and Suffix. Greatly appreciate it.
Stored Data:
Name
Walker,James M JR
Smith,Jack P
Smith,Whitney
Required result:
LastName FirstName Suffix
Walker James JR
Smith Jack
Smith Whitney
Tried Code:
select top 5 Name,
LEFT(Name, CHARINDEX(',', Name) - 1) AS LastName,
right(Name, len(Name) - CHARINDEX(',', Name)) as FirstName
Just having problem with separating First Name from Middle Initial and Suffix. Then getting Suffix from the last space from the right.
You really should store these parts of the name in separate columns (first normal form) to avoid such parsing.
You can put all the logic into one huge call of nested functions, but it is quite handy to separate them into single calls using CROSS APPLY.
The parsing is straight-forward:
find position of comma
split the string into part before comma (LastName) and part AfterComma
find position of first space in the second part AfterComma
split the string into two parts again - this gives FirstName and the rest (AfterSpace)
find position of space in AfterSpace
split the string into two parts again - this gives Initial and Suffix.
The query also checks results of CHARINDEX - it returns 0 if the string is not found.
Obviously, if the string value is not in the expected format, you'll get incorrect result.
DECLARE #T TABLE (Name varchar(8000));
INSERT INTO #T (Name) VALUES
('Walker'),
('Walker,James M JR'),
('Smith,Jack P'),
('Smith,Whitney');
SELECT
Name
,LastName
,AfterComma
,FirstName
,AfterSpace
,MidInitial
,Suffix
FROM
#T
CROSS APPLY (SELECT CHARINDEX(',', Name) AS CommaPosition) AS CA_CP
CROSS APPLY (SELECT CASE WHEN CommaPosition > 0 THEN
LEFT(Name, CommaPosition - 1) ELSE Name END AS LastName) AS CA_LN
CROSS APPLY (SELECT CASE WHEN CommaPosition > 0 THEN
SUBSTRING(Name, CommaPosition + 1, 8000) ELSE '' END AS AfterComma) AS CA_AC
CROSS APPLY (SELECT CHARINDEX(' ', AfterComma) AS SpacePosition) AS CA_SP
CROSS APPLY (SELECT CASE WHEN SpacePosition > 0 THEN
LEFT(AfterComma, SpacePosition - 1) ELSE AfterComma END AS FirstName) AS CA_FN
CROSS APPLY (SELECT CASE WHEN SpacePosition > 0 THEN
SUBSTRING(AfterComma, SpacePosition + 1, 8000) ELSE '' END AS AfterSpace) AS CA_AS
CROSS APPLY (SELECT CHARINDEX(' ', AfterSpace) AS Space2Position) AS CA_S2P
CROSS APPLY (SELECT CASE WHEN Space2Position > 0 THEN
LEFT(AfterSpace, Space2Position - 1) ELSE AfterSpace END AS MidInitial) AS CA_MI
CROSS APPLY (SELECT CASE WHEN Space2Position > 0 THEN
SUBSTRING(AfterSpace, Space2Position + 1, 8000) ELSE '' END AS Suffix) AS CA_S
result
Name LastName AfterComma FirstName AfterSpace MidInitial Suffix
Walker Walker
Walker,James M JR Walker James M JR James M JR M JR
Smith,Jack P Smith Jack P Jack P P
Smith,Whitney Smith Whitney Whitney

Invalid length parameter passed to the LEFT or SUBSTRING function in Sql Server

While trying to execute the below query
Declare #t table (id int, string varchar(1000))
INSERT INTO #t (id, string)
SELECT 1, 'zxzzxx,ppppbppp,trtrtr,tyyt,hgghh,fefew,rewr,rwerer'
;WITH test (id, lft, rght, idx)
AS
(
SELECT t.id
,LEFT(t.string, CHARINDEX(', ', t.string) - 1)
,SUBSTRING(t.string, CHARINDEX(', ', t.string) + 2, DATALENGTH(t.string))
,0
FROM #t t
UNION ALL
SELECT c.id
,CASE WHEN CHARINDEX(', ', c.rght) = 0 THEN c.rght ELSE LEFT(c.rght, CHARINDEX(', ', c.rght) - 1) END
,CASE WHEN CHARINDEX(', ', c.rght) > 0 THEN SUBSTRING(c.rght, CHARINDEX(', ', c.rght) + 2, DATALENGTH(c.rght))
ELSE '' END
,idx + 1
FROM test c
WHERE DATALENGTH(c.rght) > 0
)
select id, lft from test
I am getting the below error
Msg 537, Level 16, State 2, Line 8
Invalid length parameter passed to the LEFT or SUBSTRING function.
but the same works for SELECT 1, 'the, quick, brown, fox, jumped, over, the, lazy, dog'
Please help
It seems to be a space missing between your words.
You are currently looking for charindex of ', ' not ','.
And the string does not have any match of ', '.
This Error message happens usually when you do these steps;
When you use Substring,
left, right functions.
When you use CharIndex (used in
the field, the selected word or word
search, or the length of a character
to be inadequate)
The return value is returned to each server in the query expression, the result of the transaction 0 (zero) returns, if error returns -1
This will not result in errors for the server to return a value of -1 or are compared objects.