SQL select to get a string between two spaces - sql-server

I have a field with names in the format DOE JOHN HOWARD or DOE JOHN H.
I need a query to get the string between the two spaces (JOHN in this case).
The SO answer here shows how to do that when the desired substring is between two different strings, but I don't see how to apply something similar when the desired substring is between two instances of the same string (a space in this case).
How can I do that?

There is a somewhat sneaky way you could do this using PARSENAME.
It's intended purpose is to get particular parts of an object/namespace, however, in this case you could use it by replacing the strings with periods first.
E.g.,
SELECT PARSENAME(REPLACE('DOE JOHN HOWARD',' ','.'),2)

One way:
select
left(substring(fld,
charindex(' ', fld) + 1, len(fld)),
charindex(' ', substring(fld, charindex(' ', fld) + 2, len(fld))))

After doing some searches to resolve my problem, realised there is no generic answer, so below is piece of code to find string between some other strings. I think it may be useful for somebody in future.
DECLARE #STRING NVARCHAR(MAX) = 'Something here then stringBefore Searching stringAfter something there.'
DECLARE #FIRST NVARCHAR(20) = 'stringBefore'
DECLARE #SECOND NVARCHAR(20) = 'stringAfter'
DECLARE #SEARCHING NVARCHAR (20)
SET #SEARCHING = (SELECT SUBSTRING(#STRING, CHARINDEX(#FIRST, #STRING) + LEN(#FIRST), CHARINDEX(#SECOND, #STRING) - CHARINDEX(#FIRST, #STRING) - LEN(#FIRST)))
-- if you want to remove empty spaces
SET #SEARCHING = REPLACE(#SEARCHING, ' ', '')
SELECT #SEARCHING
Then output as below:
(No column name)
Searching

Related

How to get substring in TSQL

I want to extract special word from a sentence in SQL Server.
For example I want to extract No-13 from 'Street3 NO-13 Floor 4th'
Following code is my primary code, but I can't find the last index to get special word:
SELECT PATINDEX('%Y[^][0-9]%', 'Street3 NO-13 Floor 4th')
Fortunately I found a solution, I use following code:
DECLARE #txt NVARCHAR(255)
SET #txt = 'Street3 NO- 13 Floor 4th'
DECLARE #startIndex INT
SELECT #startIndex = PATINDEX('% No%[0-9]%',#txt)
Declare #FirstLetters AS NVARCHAR(50)
DECLARE #remainingString NVARCHAR(MAX)
SELECT #remainingString = SUBSTRING(#txt, #startIndex, LEN(#txt) - #startIndex)
SELECT #FirstLetters=SUBSTRING(#remainingString, 0, PATINDEX('%[0-9]%',#remainingString))
SELECT #remainingString=REPLACE(#remainingString,#FirstLetters,'')
SELECT #FirstLetters +LEFT(
SubString(#remainingString, PatIndex('%[0-9.-]%', #remainingString), 8000),
PatIndex('%[^0-9.-]%', SubString(#remainingString, PatIndex('%[0-9.-]%',
#remainingString), 8000) + 'X')-1) AS BuildingNo
The length of the pattern is fixed, so you always know where the pattern ends. However, it seems that what you really want is to find the end of a word that starts with a given pattern. If you only care about spaces as word separators, this is quite simple - charindex takes an optional starting location index, so you can just find the first space after the patindex result. Then you can use substring to get the string between the two indices.

SQL Server Full Text Search to match contact name to prevent duplicates

Using SQL Server Azure or 2017 with Full Text Search, I need to return possible matches on names.
Here's the simple scenario: an administrator is entering contact information for a new employee, first name, last name, address, etc. I want to be able to search the Employee table for a possible match on the name(s) to see if this employee has already been entered in the database.
This might happen as an autosuggest type of feature, or simply display some similar results, like here in Stackoverflow, while the admin is entering the data.
I need to prevent duplicates!
If the admin enters "Bob", "Johnson", I want to be able to match on:
Bob Johnson
Rob Johnson
Robert Johnson
This will give the administrator the option of seeing if this person has already been entered into the database and choose one of those choices.
Is it possible to do this type of match on words like "Bob" and include "Robert" in the results? If so, what is necessary to accomplish this?
Try this.
You need to change the #per parameter value to your requirement. It indicates how many letters out of the length of the first name should match for the result to return. I just set it to 50% for testing purposes.
The dynamic SQL piece inside the loop adds all the CHARINDEX result per letter of the first name in question, to all existing first names.
Caveats:
Repeating letters will of course be misleading, like Bob will count 3 matches in Rob because there's 2 Bs in Bob.
I didn't consider 2 first names, like Bob Robert Johnson, etc so this will fail. You can improve on that however, but you get the idea.
The final SQL query gets the LetterMatch that is greater than or equal to the set value in #per.
DECLARE #name varchar(MAX) = 'Bobby Johnson' --sample name
DECLARE #first varchar(50) = SUBSTRING(#name, 0, CHARINDEX(' ', #name)) --get the first part of the name before space
DECLARE #last varchar(50) = SUBSTRING(#name, CHARINDEX(' ', #name) + 1, LEN(#name) - LEN(#first) - 1) --get the last part of the name after space
DECLARE #walker int = 1 --for looping
DECLARE #per float = LEN(#first) * 0.50 --declare percentage of how many letters out of the length of the first name should match. I just used 50% for testing
DECLARE #char char --for looping
DECLARE #sql varchar(MAX) --for dynamic SQL use
DECLARE #matcher varchar(MAX) = '' --for dynamic SQL use
WHILE #walker <> LEN(#first) + 1 BEGIN --loop through all the letters of the first name saved in #first variable
SET #char = SUBSTRING(#first, #walker, 1) --save the current letter in the iteration
SET #matcher = #matcher + IIF(#matcher = '', '', ' + ') + 'IIF(CHARINDEX(''' + #char + ''', FirstName) > 0, 1, 0)' --build the additional column to be added to the dynamic SQL
SET #walker = #walker + 1 --move the loop
END
SET #sql = 'SELECT * FROM (SELECT FirstName, LastName, ' + #matcher + ' AS LetterMatch
FROM TestName
WHERE LastName LIKE ' + '''%' + #last + '%''' + ') AS src
WHERE CAST(LetterMatch AS int) >= ROUND(' + CAST(#per AS varchar(50)) + ', 0)'
SELECT #sql
EXEC(#sql)
SELECT * FROM tbl_Names
WHERE Name LIKE '% user defined text %';
using a text in between % % will search those text on any position in the data.

SQL concat / + operator behaving strangely?

So basically to explain my situation I have a program where a user can select code numbers, that are alpha numeric. These codes are stored in my SQL database as datatype char.
When they select all the codes they want, the program then sends a few parameters(the codes being one of them). The codes are strung together and look something like this:
',01,1,A3' etc. etc. with commas separating the codes. I have the comma in front, but changing the comma to the back does not change anything.
the #reasonCode variable is the reason codes strung together.
In my where clause I have a statement that is this:
(#reasonCode = 'ALL') OR
((#reasonCode <> 'ALL' AND (charindex(',' + ro_reason_code, #reasonCode) > 0)))
Basically I want to restrict my results to just those that have those specific reason codes the user selected(among other parameters). I am trying to achieve that by stringing together the codes, and then searching through them using charindex, seperated by commas.
However I am running into an issue. Here are the results using a few different variations of reason codes:
',1' = 625 records (correct number)
',01' = 1015(correct number)
',01,1 = 1640(correct number)
',1,01' = 1015(for whatever reason it isn't picking up the 1 reason codes)
That is my issue right there.
When I put the 1 in front of the 01, it doesn't pick up the 1 reason codes. But if I do it flip-flopped it works fine...
Any ideas as to why this happens?
(I have tried also using the concat function and get the same results, and also tried forcing everything to be char datatype.)
In the end I would like the same result, regardless if it is ,01,1 or ,1,01.
I'm pretty sure this is because you said you're using the char type instead of varchar. Try replacing your charindex expression with this:
charindex(',' + rtrim(ro_reason_code), #reasonCode)
When I used a type of char(2) in the table and char(16) for the #reasonCode, I could reproduce your result, and I found that adding the rtrim fixed the problem. But unfortunately I can't explain exactly what's going here, why having ',1' at the end of the string should work without the trim whereas having it at the beginning does not. Hopefully someone can provide a more in-depth answer that gets into the "why," but I thought I'd still post this for the time being to get you running.
Reproduction:
-- Forgive the "hackish" way of populating this table. I'm assuming sysobjects has >=1015 records.
declare #Code table (ro_reason_code char(2));
insert #Code select top 625 '1' from sysobjects;
insert #Code select top 1015 '01' from sysobjects;
declare #reasonCode char(16);
set #reasonCode = ',1,01';
select count(1) from #Code where #reasonCode = 'ALL' or charindex(',' + ro_reason_code, #reasonCode) > 0; -- Result: 1015
select count(1) from #Code where #reasonCode = 'ALL' or charindex(',' + rtrim(ro_reason_code), #reasonCode) > 0; -- Result: 1640
set #reasonCode = ',01,1';
select count(1) from #Code where #reasonCode = 'ALL' or charindex(',' + ro_reason_code, #reasonCode) > 0; -- Result: 1640
select count(1) from #Code where #reasonCode = 'ALL' or charindex(',' + rtrim(ro_reason_code), #reasonCode) > 0; -- Result: 1640
Because you are using char, which is a fixed length field, your data is stored padded out to the length of the field. So '1' is stored as '1 '
DECLARE #Code CHAR(2)
SET #Code = '1'
SELECT '''' + #Code + ''''
-- Printes '1 '
For that reason, when you add ',' to the value, you now have ',1 ' (notice the trailing whitespace)
DECLARE #Code CHAR(2)
SET #Code = '1'
SELECT '''' + ',' + #Code + ''''
-- prints ',1 '
Now if you're comparing off another char field, there will also be padded whitespace if the character data is less than the length of the field. So what appers to be ',11,1' is actually something like ',11,1 ' which does match the pattern of ',1 '
BUT, when you reverse the order, ',1,11' becomes ',1,11 ' which does not match the pattern of ',1 '
Unrelated
I just want to point out there is a subtle issue with the implementation. By only appending the leading comma, you may get false positives depending on your data. For example, ,2 will match the pattern ,25.
,2 does match 1,11,25,A01
You've gotta append the comma on both sides of each side of the evaluation.
CHARINDEX( ',' + RTRIM(ro_reason_code) + ',',
',' + RTRIM(#reasonCode) + ',') > 0
So to illustrate the difference it becomes
,2, does not match ,1,11,25,A01,

T-SQL Find length of word within a string

With PATINDEX I can find the first occourence of a pattern in a string, say a number - in the string there is several matches to my pattern
My question is how can I find the end position of the first occourence of that pattern in a string?
DECLARE #txt VARCHAR(255)
SET #txt = 'this is a string 30486240 and the string is still going 30485 and this is the end'
PRINT SUBSTRING(#txt,PATINDEX('%[0-9]%',#txt),8)
My problem is, I dont want to put in the 8 in manually, I want to find the length of the first number
Using SQL Server 2012
Try this, it should return the first number from your text:
DECLARE #txt VARCHAR(255)
SET #txt = 'this is a string 30486240 and the string is still going 30485 and this is the end'
DECLARE #startIndex INTEGER
SELECT #startIndex = PATINDEX('%[0-9]%',#txt)
DECLARE #remainingString NVARCHAR(MAX)
SELECT #remainingString = substring(#txt, #startIndex, LEN(#txt) - #startIndex)
DECLARE #endingIndex INTEGER
SELECT #endingIndex = PATINDEX('%[a-zA-Z]%', #remainingString) - 1
SELECT RTRIM(SUBSTRING(#txt, #startIndex, #endingIndex))
This query will work as long as you don't have letters "embedded" in your numbers, like 30486a24b0
Here is one solution when you don't know the length of the substring:
SELECT Left(
SubString(#Data, PatIndex('%[0-9.-]%', #Data), 8000),
PatIndex('%[^0-9.-]%', SubString(#Data, PatIndex('%[0-9.-]%', #Data), 8000) + 'X')-1)
Source: http://blogs.lessthandot.com/index.php/DataMgmt/DataDesign/extracting-numbers-with-sql-server/
I had to run through the exercise multiple times and kept thinking the blog post was wrong, before noticing the caret in the second PATINDEX.

sql to check string contains with where clause

in Sql server
I have a following string
DECLARE #str nvarchar(max);
set #str = "Hello how are you doing today,Its Monday and 5 waiting days";
DECLARE #srch nvarchar(max);
set #srch = " how,doing,monday,waiting";
Now i want to check whether str contains any of string (comma separated string) of srch
I want it in only sql server
is there possibilites to write some query with in clause
like
select from #str where _____ in (select * from CommaSplit(#srch)
where CommaSplit function rerturns rows of #srch comma separted value
I dont want to use cursor or any loop concept as the #srch value can be very long
Thanks
you can use same function to get first string in rows
select string from CommaSplit(#srch,'') where string in (select * from CommaSplit(#srch)
You can use the following common table expressions query to split your string into parts. cte will contain one record per phrase in #srch. In my example below, I show where in #str each of the search phrase is located. It returns 0 if it cannot locate a search phrase.
Note 1: it won't show the location twice if your search phrase is duplicated - you would need another CTE for that.
Note 2: I have to add comma at the end of #srch to make my CTE work. You can do that inside the CTE if you prefer not to change the search string.
DECLARE #str nvarchar(max);
set #str = 'Hello how are you doing today,Its Monday and 5 waiting days';
DECLARE #srch nvarchar(max);
set #srch = 'how,doing,monday,waiting';
set #srch = #srch + ','
-- first split the text into 1 character per row
;with cte
as
(
select substring(#srch, 1, CHARINDEX(',', #srch, 1) - 1) as Phrase, CHARINDEX(',', #srch, 1) as Idx
union all
select substring(#srch, cte.Idx + 1, CHARINDEX(',', #srch, cte.Idx + 1) - cte.Idx - 1) as Phrase, CHARINDEX(',', #srch, cte.Idx + 1) as Idx
from cte
where cte.Idx < CHARINDEX(',', #srch, cte.Idx + 1)
)
select charindex(cte.Phrase, #str, 1) from cte
I don't think that the IN clause is what you need. Instead of this you can use the LIKE construction as following:
if (select count(*) from CommaSplit(#srch) where #str like '%' + val + '%') > 0
select 'true'
else
select 'false'
In this case you will receive 'true' when at least 1 result of CommaSplit function exists in the #str text. But in this case you also will receive a 'true' value when the result of CommaSplit function is a part of the word in the #str string.
If you need more accurate solution, this can be achieved by the following way: you need to split the #str into the words (also replacing punctuation by spaces beforehand). And, after this, intersect of CommaSplit (#srch) and SpaceSplit(#str) will be the answer on the question. Among this, you also will be able to check which words are matching between two strings.
The overhead of this method is to create function SpaceSplit which is copy of CommaSplit but with another separator. Or the function CommaSplit can be modified to receive a separator as parameter.

Resources