I want to compare alphanumeric strings in same column in SQL Server - sql-server

I have a table with details of family members staying in a particular locality. Since these are government data, it has lot mistakes. Like in one column 'houseno', there a 2 values 'Ti 303' and '303' which are same house numbers.
In the end, I want Ti 303 to be updated with '303'. (As these are family members living in same house)
Similarly 'P-101' and 'P/101' are same houseno's and I want it to be converted to either 'P-101' or 'P/101'. I tried difference, substring etc but of now use to me. Please help!

You just need to strip out the characters to compare the content?
CREATE FUNCTION dbo.FN_GetNumberPart (#strMixedString VARCHAR(200))
RETURNS VARCHAR(200)
AS
BEGIN
DECLARE #NumberPart INT
-- Get the next non numeric character position
SET #NumberPart = PATINDEX('%[^0-9]%', #strMixedString)
-- While there are non numeric characters remaining
WHILE #NumberPart > 0
BEGIN
-- Remove the non numeric character from the string
SET #strMixedString = STUFF(#strMixedString, #NumberPart , 1, '' )
-- Get the next non numeric character position
SET #NumberPart = PATINDEX('%[^0-9]%', #strMixedString)
END
-- Spit out the cleansed string
RETURN ISNULL(#strMixedString,0)
END
GO
SELECT dbo.FN_GetNumberPart(HouseNo)
from TblAddresses

You should use the REPLACE command. For the two examples give you could hard code it as follows:
select REPLACE('Ti 303','Ti ','')
select REPLACE('P-101','P-','P/')
You would use REPLACE in your UPDATE command and not as a SELECT obviously.
If you have a list of strings to replace in a column with an update then you could put these into a table. Then use this in your REPLACE command for the string pattern to be replaced.

Related

Is there a way to find values that contain only 0's and a symbol of any length?

I want to find strings of any length that contain only 0's and a symbol such as a / a . or a -
Examples include 0__0 and 000/00/00000 and .00000
Considering this sample data:
CREATE TABLE dbo.things(thing varchar(255));
INSERT dbo.things(thing) VALUES
('0__0'),('000/00/00000'),('00000'),('0123456');
Try the following, which locates the first position of any character that is NOT a 0, a decimal, a forward slash, or an underscore. PATINDEX returns 0 if the pattern is not found.
SELECT thing FROM dbo.things
WHERE PATINDEX('%[^0^.^/^_]%', thing) = 0;
Results:
thing
0__0
000/00/00000
00000
The opposite:
SELECT thing FROM dbo.things
WHERE PATINDEX('%[^0^.^/^_]%', thing) > 0;
Results:
thing
0123food456
Example db<>fiddle
I can see a way of doing this... But it's something that wouldn't perform well, if you think about using it as a search criteria.
We are going to use a translate function on SQL Server, to replace the allowed characters, or symbols as you've said, with a zero. And then, eliminates the zeroes. If the result is an empty string, then there are two cases, or it only had zeroes and allowed characters, or it already was an empty string.
So, checking for this and for non-empty strings, we can define if it matches your criteria.
-- Test scenario
create table #example (something varchar(200) )
insert into #example(something) values
--Example cases from Stack Overflow
('0__0'),('000/00/00000'),('.00000'),
-- With something not allowed (don't know, just put a number)
('1230__0'),('000/04560/00000'),('.00000789'),
-- Just not allowed characters, zero, blank, and NULL
('1234567489'),('0'), (''),(null)
-- Shows the data, with a column to check if it matches your criteria
select *
from #example e
cross apply (
select case when
-- If it *must* have at least a zero
e.something like '%0%' and
-- Eliminates zeroes
replace(
-- Replaces the allowed characters with zero
translate(
e.something
,'_./'
,'000'
)
,'0'
,''
) = ''
then cast(1 as bit)
else cast(0 as bit)
end as doesItMatch
) as criteria(doesItMatch)
I really discourage you from using this as a search criteria.
-- Queries the table over this criteria.
-- This is going to compute over your entire table, so it can get very CPU intensive
select *
from #example e
where
-- If it *must* have at least a zero
e.something like '%0%' and
-- Eliminates zeroes
replace(
-- Replaces the allowed characters with zero
translate(
e.something
,'_./'
,'000'
)
,'0'
,''
) = ''
If you must use this as a search criteria, and this will be a common filter on your application, I suggest you create a new bit column, to flag if it matches this, and index it. Thus, the increase in computational effort would be spread on the inserts/updates/deletes, and the search queries won't overloading the database.
The code can be seen executing here, on DB Fiddle.
What I got from the question is that the strings must contain both 0 and any combination of the special characters in the string.
If you have SQL Server 2017 and above, you can use translate() to replace multiple characters with a space and compare this with the empty string. Also you can use LIKE to enforce that both a 0 and any combination of the special character(s) appear at least once:
DECLARE #temp TABLE (val varchar(100))
INSERT INTO #temp VALUES
('0__0'), ('000/00/00000'), ('.00000'), ('w0hee/'), ('./')
SELECT *
FROM #temp
WHERE val LIKE '%0%' --must have at least one zero somewhere
AND val LIKE '%[_/.]%' --must have at least one special character(s) somewhere
AND TRANSLATE(val, '0./_', ' ') = '' --translated zeros and sp characters to spaces equivalent to an empty string
Creates output:
val
0__0
000/00/00000
.00000

Why T sql Char() function returns values diffrent from whats specified on asciitable.com

All,I am trying to create a T sql scalar function that removes any extended ascii characters in a passed in string value.I am doing this using the PatIndex() function as below.One thing I noticed is that the T sql Char() and ascii() functions return a different character/decimal value when invoked with the correct inputs from whats mentioned in the asciitable.com .Any idea why this is diffrent? The collation used on the database(Sql server 2012) is SQL_Latin1_General_CP1_CI_AS
I use this url for copying and pasting extended ascii characters http://www.rapidtables.com/code/text/ascii-table.htm
SELECT
ASCII('Ñ') AS tsql_decimal_value,
165 AS value_from_ascii_table
SELECT
CHAR(165) AS tsql_value,
'Ñ' AS value_from_asciitable
My Tsql Function
CREATE FUNCTION dbo.udf_Remove_NON_ASCII_characters (#value AS nvarchar(2000))
RETURNS varchar(2000)
AS
BEGIN
DECLARE #incorrectcharLoc smallint --Position of bad character
DECLARE #pattern varchar(140) --Bad characters to look for
SELECT
#pattern = '%[' + CHAR(128) + .... + CHAR(255) + ']%'
SELECT
#incorrectcharLoc = PATINDEX(#pattern, #value)
WHILE #incorrectcharLoc > 0
BEGIN
SELECT
#value = STUFF(#value, #IncorrectcharLoc, 1, '')
SELECT
#IncorrectcharLoc = PATINDEX(#Pattern, #value)
END
RETURN #value
END
Please see attched screenshot for sql server result sets
Any help would be really appreciated
Having reached my own computer, I first checked the documentation on CHAR() and ASCII().
CHAR() returns an INT ASCII character code
ASCII() returns the ASCII code value of the leftmost character of a character expression
With these presuppositions and for kicks using the letter ñ, I ran the following statement:
SELECT UPPER('ñ') AS Character -- returns Ñ
, ASCII( UPPER('ñ') ) ASCII_Value -- returns 209
, CHAR( ASCII( UPPER('ñ') ) ) -- returns Ñ
, CHAR(165) AS CHAR_VALUE2 -- returns ¥
Notice that the ASCII code for the value Ñ is 209, and not 165. Also note that there are disparate unofficial versions that list the ASCII codes, such as ASCII-code.com, which does not list Ñ in the first 200 INT values. The trouble is unofficial sources.
Therefore, we can conclude:
A) Online sources should always be taken with some measure of skepticism, particularly if they are not officially sanctioned documentation (I noticed on my computer that the original source was just a picture, rather than a table)
B) The ASCII table does not have Ñ in the first 200 INT values.
One solution could be to build out your own mapping of the ASCII tables and reference that. Whatever you choose, SQL Server uses the implementation of the ISO ASCII standard, so you might as well make SQL the SOT for these issues.

Return words in between specific phrases in string in T-SQL

My column Details would return a big message such as and the only thing I want to extract is the number 874659.29. This number varies among rows but it will always comes after ,"CashAmount": and a coma (,).
There will be only one ,"CashAmount": but several comas after.
dhfgdh&%^&%,"CashAmount":874659.29,"Hasdjhf"&^%^%
Therefore, I was wondering if I could use anything to only show the number in my output column.
Thanks in advance!
Here is another option for this just using some string manipulation.
declare #Details varchar(100) = 'dhfgdh&%^&%,"CashAmount":874659.29,"Hasdjhf"&^%^%'
select left(substring(#Details, CHARINDEX('CashAmount":', #Details) + 12 /*12 is the length of CashAmount":*/, LEN(#Details))
, charindex(',', substring(#Details, CHARINDEX('CashAmount":', #Details) + 12, LEN(#Details))) - 1)
You could use one of the split string functions as described here..
declare #string varchar(max)
set #string='dhfgdh&%^&%,"CashAmount":874659.29,"Hasdjhf"&^%^%'
select b.val from
[dbo].[SplitStrings_Numbers](#string,',')a
cross apply
(
select isnumeric(replace(a.item,'"CashAmount":',1)),replace(a.item,'"CashAmount":',1)
) b(chk,val)
where b.chk=1
Output:
874659.29
The above will work only if number comes after cashamount and before , and if it doesn't have any special characters..
if your number has special characters,you can use TRY_PARSE and check for NULL..

SQL Select command to selectively extract data from a string

I have data in a column which contains values for various fields from an application and all these fields are concatenated into one field on the database side and separated with commas.
If the field in the application is blank, then the value between the two commas will just be blank.
I need a select statement to select each of the individual fields if they are populated. I would like to specify each field as a variable which I will declare at the top of the statement.
An example of the string in the database field is:
,"FIELD1","FIELD2","FIELD3",FIELD4,FIELD5,FIELD6,,,,"FIELD10",FIELD11,FIELD12,FIELD13
As you can see, fields 7-9 were blank in this example so they are blank in the string.
I just need a way to selectively select the field I need using the commas as my marker. The string always starts with a comma so field1 always comes after the first comma.
I hope this makes sense!
Try this:
DECLARE #STRING VARCHAR(255) = ',"FIELD1","FIELD2","FIELD3",FIELD4,FIELD5,FIELD6,,,,"FIELD10",FIELD11,FIELD12,FIELD13'
DECLARE #FieldToReturn INT = 12 -- Pick which field you want
SET #STRING = RIGHT(#STRING, LEN(#STRING) - 1) + ',' -- Strip leading comma & add comma to the end
WHILE #FieldToReturn > 1
BEGIN
SET #STRING = SUBSTRING(#STRING,PATINDEX('%,%',#STRING), LEN(#STRING))
SET #FieldToReturn = #FieldToReturn - 1
SET #STRING = RIGHT(#STRING, LEN(#STRING) - 1)
END
SELECT SUBSTRING(#STRING,0,PATINDEX('%,%', #STRING))
If it the field is not populated, this will return a blank.
Edit: I know that I could have put all of the string manipulation in one line within the WHILE, but chose not to for readability...to me that is more important that the possibility of a teeny tiny bit of overhead in this example

Find index of last occurrence of a sub-string using T-SQL

Is there a straightforward way of finding the index of the last occurrence of a string using SQL? I am using SQL Server 2000 right now. I basically need the functionality that the .NET System.String.LastIndexOf method provides. A little googling revealed this - Function To Retrieve Last Index - but that does not work if you pass in a "text" column expression. Other solutions found elsewhere work only so long as the text you are searching for is 1 character long.
I will probably have to cook a function up. If I do so, I will post it here so you folks can look at it and maybe make use of.
Straightforward way? No, but I've used the reverse. Literally.
In prior routines, to find the last occurence of a given string, I used the REVERSE() function, followed CHARINDEX, followed again by REVERSE to restore the original order. For instance:
SELECT
mf.name
,mf.physical_name
,reverse(left(reverse(physical_name), charindex('\', reverse(physical_name)) -1))
from sys.master_files mf
shows how to extract the actual database file names from from their "physical names", no matter how deeply nested in subfolders. This does search for only one character (the backslash), but you can build on this for longer search strings.
The only downside is, I don't know how well this will work on TEXT data types. I've been on SQL 2005 for a few years now, and am no longer conversant with working with TEXT -- but I seem to recall you could use LEFT and RIGHT on it?
Philip
The simplest way is....
REVERSE(SUBSTRING(REVERSE([field]),0,CHARINDEX('[expr]',REVERSE([field]))))
If you are using Sqlserver 2005 or above, using REVERSE function many times is detrimental to performance, below code is more efficient.
DECLARE #FilePath VARCHAR(50) = 'My\Super\Long\String\With\Long\Words'
DECLARE #FindChar VARCHAR(1) = '\'
-- text before last slash
SELECT LEFT(#FilePath, LEN(#FilePath) - CHARINDEX(#FindChar,REVERSE(#FilePath))) AS Before
-- text after last slash
SELECT RIGHT(#FilePath, CHARINDEX(#FindChar,REVERSE(#FilePath))-1) AS After
-- the position of the last slash
SELECT LEN(#FilePath) - CHARINDEX(#FindChar,REVERSE(#FilePath)) + 1 AS LastOccuredAt
You are limited to small list of functions for text data type.
All I can suggest is start with PATINDEX, but work backwards from DATALENGTH-1, DATALENGTH-2, DATALENGTH-3 etc until you get a result or end up at zero (DATALENGTH-DATALENGTH)
This really is something that SQL Server 2000 simply can't handle.
Edit for other answers : REVERSE is not on the list of functions that can be used with text data in SQL Server 2000
DECLARE #FilePath VARCHAR(50) = 'My\Super\Long\String\With\Long\Words'
DECLARE #FindChar VARCHAR(1) = '\'
SELECT LEN(#FilePath) - CHARINDEX(#FindChar,REVERSE(#FilePath)) AS LastOccuredAt
Old but still valid question, so heres what I created based on the info provided by others here.
create function fnLastIndexOf(#text varChar(max),#char varchar(1))
returns int
as
begin
return len(#text) - charindex(#char, reverse(#text)) -1
end
REVERSE(SUBSTRING(REVERSE(ap_description),CHARINDEX('.',REVERSE(ap_description)),len(ap_description)))
worked better for me
This worked very well for me.
REVERSE(SUBSTRING(REVERSE([field]), CHARINDEX(REVERSE('[expr]'), REVERSE([field])) + DATALENGTH('[expr]'), DATALENGTH([field])))
Hmm, I know this is an old thread, but a tally table could do this in SQL2000 (or any other database):
DECLARE #str CHAR(21),
#delim CHAR(1)
SELECT #str = 'Your-delimited-string',
#delim = '-'
SELECT
MAX(n) As 'position'
FROM
dbo._Tally
WHERE
substring(#str, _Tally.n, 1) = #delim
A tally table is just a table of incrementing numbers.
The substring(#str, _Tally.n, 1) = #delim gets the position of each delimiter, then you just get the maximum position in that set.
Tally tables are awesome. If you haven't used them before, there is a good article on SQL Server Central.
*EDIT: Removed n <= LEN(TEXT_FIELD), as you can't use LEN() on the TEXT type. As long as the substring(...) = #delim remains though the result is still correct.
This answer uses MS SQL Server 2008 (I don't have access to MS SQL Server 2000), but the way I see it according to the OP are 3 situations to take into consideration. From what I've tried no answer here covers all 3 of them:
Return the last index of a search character in a given string.
Return the last index of a search sub-string (more than just a single
character) in a given string.
If the search character or sub-string is not in the given string return 0
The function I came up with takes 2 parameters:
#String NVARCHAR(MAX) : The string to be searched
#FindString NVARCHAR(MAX) : Either a single character or a sub-string to get the last
index of in #String
It returns an INT that is either the positive index of #FindString in #String or 0 meaning that #FindString is not in #String
Here's an explanation of what the function does:
Initializes #ReturnVal to 0 indicating that #FindString is not in #String
Checks the index of the #FindString in #String by using CHARINDEX()
If the index of #FindString in #String is 0, #ReturnVal is left as 0
If the index of #FindString in #String is > 0, #FindString is in #String so
it calculates the last index of #FindString in #String by using REVERSE()
Returns #ReturnVal which is either a positive number that is the last index of
#FindString in #String or 0 indicating that #FindString is not in #String
Here's the create function script (copy and paste ready):
CREATE FUNCTION [dbo].[fn_LastIndexOf]
(#String NVARCHAR(MAX)
, #FindString NVARCHAR(MAX))
RETURNS INT
AS
BEGIN
DECLARE #ReturnVal INT = 0
IF CHARINDEX(#FindString,#String) > 0
SET #ReturnVal = (SELECT LEN(#String) -
(CHARINDEX(REVERSE(#FindString),REVERSE(#String)) +
LEN(#FindString)) + 2)
RETURN #ReturnVal
END
Here's a little bit that conveniently tests the function:
DECLARE #TestString NVARCHAR(MAX) = 'My_sub2_Super_sub_Long_sub1_String_sub_With_sub_Long_sub_Words_sub2_'
, #TestFindString NVARCHAR(MAX) = 'sub'
SELECT dbo.fn_LastIndexOf(#TestString,#TestFindString)
I have only run this on MS SQL Server 2008 because I don't have access to any other version but from what I've looked into this should be good for 2008+ at least.
Enjoy.
Reverse both your string and your substring, then search for the first occurrence.
If you want to get the index of the last space in a string of words, you can use this expression
RIGHT(name, (CHARINDEX(' ',REVERSE(name),0)) to return the last word in the string. This is helpful if you want to parse out the last name of a full name that includes initials for the first and /or middle name.
Some of the other answers return an actual string whereas I had more need to know the actual index int. And the answers that do that seem to over-complicate things. Using some of the other answers as inspiration, I did the following...
First, I created a function:
CREATE FUNCTION [dbo].[LastIndexOf] (#stringToFind varchar(max), #stringToSearch varchar(max))
RETURNS INT
AS
BEGIN
RETURN (LEN(#stringToSearch) - CHARINDEX(#stringToFind,REVERSE(#stringToSearch))) + 1
END
GO
Then, in your query you can simply do this:
declare #stringToSearch varchar(max) = 'SomeText: SomeMoreText: SomeLastText'
select dbo.LastIndexOf(':', #stringToSearch)
The above should return 23 (the last index of ':')
Hope this made it a little easier for someone!
I realize this is a several years old question, but...
On Access 2010, you can use InStrRev() to do this. Hope this helps.
I know that it will be inefficient but have you considered casting the text field to varchar so that you can use the solution provided by the website you found? I know that this solution would create issues as you could potentially truncate the record if the length in the text field overflowed the length of your varchar (not to mention it would not be very performant).
Since your data is inside a text field (and you are using SQL Server 2000) your options are limited.
#indexOf = <whatever characters you are searching for in your string>
#LastIndexOf = LEN([MyField]) - CHARINDEX(#indexOf, REVERSE([MyField]))
Haven't tested, it might be off by one because of zero index, but works in SUBSTRING function when chopping off from #indexOf characters to end of your string
SUBSTRING([MyField], 0, #LastIndexOf)
This code works even if the substring contains more than 1 character.
DECLARE #FilePath VARCHAR(100) = 'My_sub_Super_sub_Long_sub_String_sub_With_sub_Long_sub_Words'
DECLARE #FindSubstring VARCHAR(5) = '_sub_'
-- Shows text before last substing
SELECT LEFT(#FilePath, LEN(#FilePath) - CHARINDEX(REVERSE(#FindSubstring), REVERSE(#FilePath)) - LEN(#FindSubstring) + 1) AS Before
-- Shows text after last substing
SELECT RIGHT(#FilePath, CHARINDEX(REVERSE(#FindSubstring), REVERSE(#FilePath)) -1) AS After
-- Shows the position of the last substing
SELECT LEN(#FilePath) - CHARINDEX(REVERSE(#FindSubstring), REVERSE(#FilePath)) AS LastOccuredAt
I needed to find the nth last position of a backslash in a folder path.
Here is my solution.
/*
http://stackoverflow.com/questions/1024978/find-index-of-last-occurrence-of-a-sub-string-using-t-sql/30904809#30904809
DROP FUNCTION dbo.GetLastIndexOf
*/
CREATE FUNCTION dbo.GetLastIndexOf
(
#expressionToFind VARCHAR(MAX)
,#expressionToSearch VARCHAR(8000)
,#Occurrence INT = 1 -- Find the nth last
)
RETURNS INT
AS
BEGIN
SELECT #expressionToSearch = REVERSE(#expressionToSearch)
DECLARE #LastIndexOf INT = 0
,#IndexOfPartial INT = -1
,#OriginalLength INT = LEN(#expressionToSearch)
,#Iteration INT = 0
WHILE (1 = 1) -- Poor man's do-while
BEGIN
SELECT #IndexOfPartial = CHARINDEX(#expressionToFind, #expressionToSearch)
IF (#IndexOfPartial = 0)
BEGIN
IF (#Iteration = 0) -- Need to compensate for dropping out early
BEGIN
SELECT #LastIndexOf = #OriginalLength + 1
END
BREAK;
END
IF (#Occurrence > 0)
BEGIN
SELECT #expressionToSearch = SUBSTRING(#expressionToSearch, #IndexOfPartial + 1, LEN(#expressionToSearch) - #IndexOfPartial - 1)
END
SELECT #LastIndexOf = #LastIndexOf + #IndexOfPartial
,#Occurrence = #Occurrence - 1
,#Iteration = #Iteration + 1
IF (#Occurrence = 0) BREAK;
END
SELECT #LastIndexOf = #OriginalLength - #LastIndexOf + 1 -- Invert due to reverse
RETURN #LastIndexOf
END
GO
GRANT EXECUTE ON GetLastIndexOf TO public
GO
Here are my test cases which pass
SELECT dbo.GetLastIndexOf('f','123456789\123456789\', 1) as indexOf -- expect 0 (no instances)
SELECT dbo.GetLastIndexOf('\','123456789\123456789\', 1) as indexOf -- expect 20
SELECT dbo.GetLastIndexOf('\','123456789\123456789\', 2) as indexOf -- expect 10
SELECT dbo.GetLastIndexOf('\','1234\6789\123456789\', 3) as indexOf -- expect 5
To get the part before the last occurence of the delimiter (works only for NVARCHAR due to DATALENGTH usage):
DECLARE #Fullstring NVARCHAR(30) = '12.345.67890.ABC';
DECLARE #Delimiter CHAR(1) = '.';
SELECT SUBSTRING(#Fullstring, 1, DATALENGTH(#Fullstring)/2 - CHARINDEX(#Delimiter, REVERSE(#Fullstring)));
This answer meets the requirements of the OP. specifically it allows the needle to be more than a single character and it does not generate an error when needle is not found in haystack. It seemed to me that most (all?) of the other answers did not handle those edge cases. Beyond that I added the "Starting Position" argument provided by the native MS SQL server CharIndex function. I tried to exactly mirror the specification for CharIndex except to process right to left instead of left to right. eg I return null if either needle or haystack is null and I return zero if needle is not found in haystack. One thing that I could not get around is that with the built in function the third parameter is optional. With SQL Server user defined functions, all parameters must be provided in the call unless the function is called using "EXEC" . While the third parameter must be included in the parameter list, you can provide the keyword "default" as a placeholder for it without having to give it a value (see examples below). Since it is easier to remove the third parameter from this function if not desired than it would be to add it if needed I have included it here as a starting point.
create function dbo.lastCharIndex(
#needle as varchar(max),
#haystack as varchar(max),
#offset as bigint=1
) returns bigint as begin
declare #position as bigint
if #needle is null or #haystack is null return null
set #position=charindex(reverse(#needle),reverse(#haystack),#offset)
if #position=0 return 0
return (len(#haystack)-(#position+len(#needle)-1))+1
end
go
select dbo.lastCharIndex('xyz','SQL SERVER 2000 USES ANSI SQL',default) -- returns 0
select dbo.lastCharIndex('SQL','SQL SERVER 2000 USES ANSI SQL',default) -- returns 27
select dbo.lastCharIndex('SQL','SQL SERVER 2000 USES ANSI SQL',1) -- returns 27
select dbo.lastCharIndex('SQL','SQL SERVER 2000 USES ANSI SQL',11) -- returns 1
I came across this thread while searching for a solution to my similar problem which had the exact same requirement but was for a different kind of database that was also lacking the REVERSE function.
In my case this was for a OpenEdge (Progress) database, which has a slightly different syntax. This made the INSTR function available to me that most Oracle typed databases offer.
So I came up with the following code:
SELECT
INSTR(foo.filepath, '/',1, LENGTH(foo.filepath) - LENGTH( REPLACE( foo.filepath, '/', ''))) AS IndexOfLastSlash
FROM foo
However, for my specific situation (being the OpenEdge (Progress) database) this did not result into the desired behaviour because replacing the character with an empty char gave the same length as the original string. This doesn't make much sense to me but I was able to bypass the problem with the code below:
SELECT
INSTR(foo.filepath, '/',1, LENGTH( REPLACE( foo.filepath, '/', 'XX')) - LENGTH(foo.filepath)) AS IndexOfLastSlash
FROM foo
Now I understand that this code won't solve the problem for T-SQL because there is no alternative to the INSTR function that offers the Occurence property.
Just to be thorough I'll add the code needed to create this scalar function so it can be used the same way like I did in the above examples.
-- Drop the function if it already exists
IF OBJECT_ID('INSTR', 'FN') IS NOT NULL
DROP FUNCTION INSTR
GO
-- User-defined function to implement Oracle INSTR in SQL Server
CREATE FUNCTION INSTR (#str VARCHAR(8000), #substr VARCHAR(255), #start INT, #occurrence INT)
RETURNS INT
AS
BEGIN
DECLARE #found INT = #occurrence,
#pos INT = #start;
WHILE 1=1
BEGIN
-- Find the next occurrence
SET #pos = CHARINDEX(#substr, #str, #pos);
-- Nothing found
IF #pos IS NULL OR #pos = 0
RETURN #pos;
-- The required occurrence found
IF #found = 1
BREAK;
-- Prepare to find another one occurrence
SET #found = #found - 1;
SET #pos = #pos + 1;
END
RETURN #pos;
END
GO
To avoid the obvious, when the REVERSE function is available you do not need to create this scalar function and you can just get the required result like this:
SELECT
LEN(foo.filepath) - CHARINDEX('/', REVERSE(foo.filepath))+1 AS LastIndexOfSlash
FROM foo
handles lookinng for something > 1 char long.
feel free to increase the parm sizes if you like.
couldnt resist posting
drop function if exists lastIndexOf
go
create function lastIndexOf(#searchFor varchar(100),#searchIn varchar(500))
returns int
as
begin
if LEN(#searchfor) > LEN(#searchin) return 0
declare #r varchar(500), #rsp varchar(100)
select #r = REVERSE(#searchin)
select #rsp = REVERSE(#searchfor)
return len(#searchin) - charindex(#rsp, #r) - len(#searchfor)+1
end
and tests
select dbo.lastIndexof('greg','greg greg asdflk; greg sadf' ) -- 18
select dbo.lastIndexof('greg','greg greg asdflk; grewg sadf' ) --5
select dbo.lastIndexof(' ','greg greg asdflk; grewg sadf' ) --24
This thread has been going for a while. I'll offer a solution covering different basis with example:
declare #aStringData varchar(100) = 'The quick brown/fox jumps/over the/lazy dog.pdf'
/*
The quick brown/fox jumps/over the/lazy dog.pdf
fdp.god yzal/eht revo/spmuj xof/nworb kciuq ehT
*/
select
Len(#aStringData) - CharIndex('/', Reverse(#aStringData)) + 1 [Char Index],
-- Get left side of character, without the character '/'
Left(#aStringData, Len(#aStringData) - CharIndex('/', Reverse(#aStringData))) [Left excluding char],
-- Get left side of character, including the character '/'
Left(#aStringData, Len(#aStringData) - CharIndex('/', Reverse(#aStringData)) + 1) [Left including char],
-- Get right side of character, without the character '/'
Right(#aStringData, CharIndex('/', Reverse(#aStringData)) - 1) [Right including char]
To get char position, need to reverse the string as CharIndex gets the first occurrence. Remembering as we're reversing, the CharIndex cursor will land on the other side of the character we're finding. So expect to compensate by -1 or +1, depending if wanting to get left or right side portion of string.

Resources