Replace substring between two delimiters or until the end of string - sql

In a procedure in SQL Server 2008 I need to replace a substring between 2 identifiers. I do not know the complete strings to be replaced, or if the second identifier exists.
If the terminator identifier is not present, I would need the end of the string to be considered one. Also, the identifiers could be the same.
DECLARE #startIdenfier VARCHAR(10) = 'the'
DECLARE #endIdenfier VARCHAR(10) = 'the'
DECLARE #newString VARCHAR(20) = 'new string that'
Sample input / output:
'This is the old string that the process needs to be applied on.' ->
'This is the new string that the process needs to be applied on.'
'This is the old string' ->
'This is the new string that'
SET #endIdenfier = 'I'
'This is the old string that I don't like' ->
'This is the new string that I don't like''
It is a generic replace and I havn't been able to find the proper way to do it, since the REPLACE function does not accept indexes.
EDIT: This community is awesome. I am sorry I cannot select multiple accepted solutions, but I thank you all for your help. I'll try all solutions already posted (besides the already accepted one which I tested) and up-vote individually.

--Find the start index plus the length of the string found (plus one for the space)
SET #startIdx = CHARINDEX(#startIdentifier, #initialString, 1) + LEN(#startIdentifier) + 1
--Find the next occurrence of the end identifier (minus one for the space)
SET #endIdx = CHARINDEX(#endIdentifier, #initialString, #startIdx) - 1;
--end not found?
IF #endIdx = -1 SET #endIdx = LEN(#initialString) + 1;
--Use the STUFF function to remove the old chars from endindex-startindex, and insert the new string at the startindex
SET #results = STUFF(#initialString, #startIdx, #endIdx - #startIdx, #newString)
In full:
DECLARE #startIdenfier Varchar(10)
SET #startIdenfier = 'the'
DECLARE #endIdenfier Varchar(10)
SET #endIdenfier = 'the'
DECLARE #newString Varchar(100)
SET #newString = 'new string that'
DECLARE #initialString VARCHAR(256) = 'this is the old string that the process needs to be applied on';
DECLARE #startIdx INT;
SET #startIdx = CHARINDEX(#startIdenfier, #initialString, 1) + LEN(#startIdenfier) + 1;
DECLARE #endIdx INT;
SET #endIdx= CHARINDEX(#endIdenfier, #initialString, #startIdx) - 1;
IF #endIdx = -1 SET #endIdx = LEN(#initialString) + 1;
DECLARE #results VARCHAR(256);
SET #results = STUFF(#initialString, #startIdx, #endIdx - #startIdx, #newString);
SELECT #results

You could do something like this:
/*Declare necessary variables*/
DECLARE #startIndex INT
DECLARE #endIndex INT
DECLARE #startReplace INT
DECLARE #lengthReplace INT
DECLARE #replaceString VARCHAR(500)
/*Get the index of the start/end idenfier*/
SELECT #startIndex = CHARINDEX ( #startIdenfier , #originalString)
SELECT #endIndex = CHARINDEX ( #startIdenfier , #originalString, #startIndex+1)
/*In case the end idenfier doesn't exist*/
IF #endIndex = 0
SET #endIndex = LEN(#originalString) + 1
SET #startReplace = #startIndex + len(#startIdenfier)
SET #lengthReplace = #endIndex - #startReplace
SELECT STUFF(#originalString, #startReplace, #lengthReplace, #newString)

I think you will need to incorporate the PATINDEX function and create the pattern by using your start and end identifiers. That will satisfy the condition where both start and end identifiers are present...
DECLARE #OldString nvarchar(max)
DECLARE #NewString nvarchar(max)
DECLARE #StartLocation bigint
DECLARE #Pattern nvarchar(200) = '%' + #StartIdentifier + ' % ' + #EndIdentifer + '%'
SELECT #StartLocation = PATINDEX(#Pattern, 'old complete string')
If the pattern is located, then you can get the string to be replaced by substringing the 'old complete string', starting at position (#StartLocation + Length of #StartIdentifier + 1). To determine the length for SUBSTRING, you need to locate the position of #EndIdentifier, using CHARINDEX of the old complete string beginning at ( #StartLocation + Length of #StartIdentifier + 1). Subtract ( #StartLocation + Length of StartIdentifier + 1) from result of CHARINDEX.
SELECT #OldString = SUBSTRING('complete old string', #StartLocation + LEN(#StartIdentifier) + 1, CHARINDEX(' ' + #EndIdentifier, 'old complete string', #StartLocation + LEN(#StartIdentifier) + 1) - (#StartLocation + LEN(#StartIdentifier) + 1)))
You can at this point do a straight forward REPLACE to get the new string.
SELECT #NewCompleteString = REPLACE('old complete string', #OldString, #NewString)
"If there is no 'terminator' identifier, I would need the end of the
string to be considered one."
If the initial pattern was not located, we fall back to search for the #StartIdentifier only. For this you can reset the pattern to contain the #StartIdentifier only...
SELECT #Pattern = '%' + #StartIdentifier + ' %'
SELECT #StartLocation = PATINDEX(#Pattern, 'old complete string')
If the pattern is located then you can get the old string to replace by SUBSTRING starting at ( #StartLocation + Length of #StartIdentifier + 1 ), with a length of 'old complete string' length - ( #StartLocation + Length of #StartIdentifier + 1 )...
SELECT #OldString = SUBSTRING('old complete string', #StartLocation + LEN(#StartIdentifier) + 1, LEN('old complete string') - (#StartLocation + LEN(#StartIdentifier) + 1))
You can then REPLACE...
SELECT #NewCompleteString = REPLACE('old complete string', #OldString, #NewString)

Something like this...
DECLARE #initialString VARCHAR(32) = '1234567890123456789'
DECLARE #startIdentifier VARCHAR(32) = '34'
DECLARE #endIdentifier VARCHAR(32) = '34'
DECLARE #newString VARCHAR(32) = 'ABC'
DECLARE #headChars INT = CHARINDEX(#startIdentifier, #initialString, 1)
IF #headChars > 0
SET #headChars = #headChars + LEN(#startIdentifier) - 1
DECLARE #bodyChars INT = CHARINDEX(#endIdentifier, #initialString, #headChars + 1)
IF #bodyChars > 0
SET #bodyChars = LEN(#initialString) - #bodyChars + 1
SELECT
LEFT(#initialString, #headChars)
+ #newString
+ RIGHT(#initialString, #bodyChars)
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=b8a179d0e63840dfa60905d9951e4b22
For example...
'1234567890123456789'
'34' => Start # 1 => Found # 3 => keep chars 1->4
'34' => Start # 5 => Found # 13 => keep chars 13->end

Just use STUFF and CHARINDEX. Figure out:
the position at which replacement begins (position of the + length of the)
the position at which replacement ends (position of the starting from see above)
And subtract the positions to calculate the number of characters to replace.
DECLARE #string VARCHAR(100) = 'This is the old string that the process needs to be applied on.'
DECLARE #replace VARCHAR(100) = 'NEW STRING THAT '
DECLARE #delim1 VARCHAR(100) = 'the '
DECLARE #delim2 VARCHAR(100) = 'the '
DECLARE #pos1 INT = CHARINDEX(#delim1, #string) + DATALENGTH(#delim1)
DECLARE #pos2 INT = ISNULL(NULLIF(CHARINDEX(#delim2, #string, #pos1), 0), DATALENGTH(#string) + 1)
SELECT STUFF(#string, #pos1, #pos2 - #pos1, #replace)
-- "This is the NEW STRING THAT the process needs to be applied on."
SET #delim2 = 'xxx'
SET #pos1 = CHARINDEX(#delim1, #string) + DATALENGTH(#delim1)
SET #pos2 = ISNULL(NULLIF(CHARINDEX(#delim2, #string, #pos1), 0), DATALENGTH(#string) + 1)
SELECT STUFF(#string, #pos1, #pos2 - #pos1, #replace)
-- "This is the NEW STRING THAT "
Note: spaces should be a part of search delimiters instead of the logic. the should not match them and threfore.

Related

Replace the alternate occurances of a substring

My input strings are like:
A or B OR C or D OR E or F
A OR B OR C OR D OR E OR F
Expected Output: 'A or B' OR 'C or D' OR 'E or F'
outputString = '''' + REPLACE(#inputValue COLLATE Latin1_General_CS_AS, ' OR ' COLLATE Latin1_General_CS_AS, ''' OR ''') + ''''
I tried using SQL Replace function and the above statement works properly for the first string and I get the desired output but for the second string since we have all the ORs in the uppercase it fails and returns 'A' OR 'B' OR 'C' OR 'D' OR 'E' OR 'F'
I'm using SSMS 15.0.
How can I solve this problem? Any help will be appreciated.
Here's a solution that uses a UDF.
The function splits a string on a pattern as a resultset.
(similar as the STRING_SPLIT function, but with a pattern)
The FOR XML trick is then used to construct a string from the splitted parts, and to add the quotes.
DECLARE #vchNewValue VARCHAR(100), #result VARCHAR(100);
SET #vchNewValue = 'A OR B or C OR D or E OR F';
SET #result = LTRIM(RTRIM((
SELECT
CASE WHEN match = 1
THEN ' '+quotename(ltrim(rtrim(replace(value,' OR ',' or ') )),'''')+' '
ELSE UPPER(value)
END
FROM dbo.fnPattern_Split(' '+#vchNewValue+' ', ' % OR % ') AS spl
ORDER BY ordinal
FOR XML PATH(''), TYPE).value(N'./text()[1]', N'nvarchar(max)')
));
SELECT #result AS result;
result
'A or B' OR 'C or D' OR 'E or F'
Test db<>fiddle here
The UDF
Uses PATINDEX to find each next start position of the given pattern in the string.
Then finds the nearest end position where the pattern is still valid.
So it's kinda like a lazy search in regex.
The positions are then used to insert the parts into the returned table.
CREATE FUNCTION dbo.fnPattern_Split
(
#str VARCHAR(MAX),
#pattern VARCHAR(100)
)
RETURNS #tbl TABLE (
ordinal INT,
value VARCHAR(MAX),
match BIT
)
WITH SCHEMABINDING
AS
BEGIN
DECLARE #value NVARCHAR(MAX)
, #splitvalue NVARCHAR(MAX)
, #startpos INT = 0
, #endpos INT = 0
, #ordinal INT = 0
, #foundend BIT = 0
, #patminlen INT = ISNULL(NULLIF(LEN(REPLACE(#pattern,'%','')),0),1);
WHILE (LEN(#str) > 0)
BEGIN
SET #startpos = ISNULL(NULLIF(PATINDEX('%'+#pattern+'%', #str),0), LEN(#str)+1);
IF #startpos < LEN(#str)
BEGIN
SET #foundend = 0;
SET #endpos = #startpos+#patminlen-1;
WHILE #endpos < LEN(#str) AND #foundend = 0
BEGIN
IF SUBSTRING(#str, #startpos, 1+#endpos-#startpos) LIKE #pattern
SET #foundend = 1;
ELSE
SET #endpos += 1;
END
END
ELSE SET #endpos = LEN(#str);
IF #startpos > 1
BEGIN
SET #ordinal += 1;
SET #value = LEFT(#str, #startpos-1);
INSERT INTO #tbl (ordinal, value, match)
VALUES (#ordinal, #value, 0);
END
IF #endpos >= #startpos
BEGIN
SET #ordinal += 1;
SET #splitvalue = SUBSTRING(#str, #startpos, 1+#endpos-#startpos);
INSERT INTO #tbl (ordinal, value, match)
VALUES (#ordinal, #splitvalue, 1);
END
SET #str = SUBSTRING(#str, #endpos+1, LEN(#str));
END;
RETURN;
END;
A recursive solution that stuffs the quotes.
The recursive CTE loops through the string while finding the start positions of the ' or ' patterns.
Since ' or ' has 4 characters, having a start position means you also have the end position.
TheSTUFF function can insert characters in a string on positions.
So the positions are used to stuff the quotes where needed.
Which is every even occurence (modulus 2 of lvl is 0).
declare #input varchar(100)
, #result varchar(100);
set #input = 'A OR B or C OR D or E OR F';
set #result = #input;
with rcte as (
select 1 as lvl
, charindex(' or ', #input) as pos
, len(#input) as max_pos
union all
select lvl+1
, isnull(nullif(charindex(' or ', #input, pos+4), 0), max_pos)
, max_pos
from rcte
where pos < max_pos
)
select #result = stuff(stuff(#result,pos+4,0,''''),pos,0,'''')
from rcte
where lvl%2 = 0 and pos+4 < max_pos
order by lvl desc;
SET #result = ''''+#result+'''';
SET #result = REPLACE(REPLACE(#result,' OR ',' or '),''' or ''',''' OR ''');
select #result as result;
result
'A or B' OR 'C or D' OR 'E or F'
Test on db<>fiddle here
This solution comes with a high cost on server load since while is used.
declare #input varchar(100)
set #input = 'A or B or C or D or E or F or G or'
declare #inc int = 1, #end int = 1
,#final varchar(100) = '', #part varchar(100)
,#nextposition varchar(100), #or varchar(10)= ''
,#last varchar(10), #ifendsOR varchar(10)
select #nextposition = case when #input like '%or' then substring(#input,1,len(#input)-2) else #input end
select #ifendsOR = case when #input like '%or' then ' or' else '' end
select #last = ltrim(rtrim(right(#nextposition,2)))
while #end <> 0
begin
select #part = substring(#nextposition,1,charindex('or',#nextposition)-2)
select #nextposition = replace(#nextposition,concat(#part,' or '),'')
set #end = charindex('or',#nextposition)
select #or = case when #inc%2 = 0 then ' OR ' else ' or ' end
set #inc = #inc+1
set #final = concat(#final,#part,#or)
end
select #ifendsOR = case when #inc%2 = 0 then upper(#ifendsOR) else #ifendsOR end
select concat(#final,#last,#ifendsOR)

Check a chunk of text for tilde and ensure they are 60 characters apart. If not place a tilde inbetween

I currently have a function in SQL that places a tilde every 60 characters, but the original text already has tilde's so , basically I want to change that if there is a tilde and the next tilde is under 60 characters away, then skip to the next tilde. If its over 60 characters, then only then place a extra tilde.
My current function looks like;
function [dbo].[AddTilde] (
#string varchar(max),
#count int
)
returns varchar(max)
as
begin
declare #result varchar(max) = ''
declare #token varchar(max) = ''
while DATALENGTH(#string) > 0
begin
select #token = left(#string, #count)
select #string = REPLACE(#string, #token, '')
select #result +=#token +case when DATALENGTH(#string)=0 then '' else '~' end
end
return #result
end
Any help appreciated
Many Thanks
DECLARE #string1 VARCHAR(max),
#string2 VARCHAR(max) = '',
#i1 INT,
#i2 INT
SET #string1 = '12345678901234567890~1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890'
WHILE LEN(#string1) > 0
BEGIN
SET #i1 = CHARINDEX('~',#string1)
SET #i2 = #i1 - (ABS(#i1-60) + (#i1-60)) / 2 -- MINIMUM OF ~ LOCATION AND 60
SET #i1 = LEN(#string1)
IF #i2 = 0 SET #i2 = #i1 - (ABS(#i1-60) + (#i1-60)) / 2 -- MINIMUM OF LENGTH OF #string1 LOCATION AND 60
IF #i2 < 60
BEGIN
SET #string2 = #string2 + LEFT(#string1,#i2)
SET #string1 = RIGHT(#string1,#i1-#i2)
END
ELSE
BEGIN
SET #string2 = #string2 + LEFT(#string1,60) + '~'
SET #string1 = RIGHT(#string1,#i1-60)
END
END
Results: 12345678901234567890~123456789012345678901234567890123456789012345678901234567890~1234567890123456789012345678901234567890
This is based on my String Splitting function, which has a very good performance.
This function should be quite efficient, albeit hard to understand (I have added a few commenets in attempt to make it easier).
You can change internal parameters easily e.g. #Delimitor can be multiple characters
Test cases are included at the bottom.
ALTER FUNCTION [dbo].[AddTilde]
(
#String VARCHAR( MAX ),
#Count INT
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #CurrentPosition BIGINT, #NextDelimiterPosition BIGINT, #NextFixedPosition BIGINT, #NextPosition BIGINT,
#DelimiterLength INT, #Delimiter VARCHAR( 5 ), #Result VARCHAR( MAX )
-- Initialise
SET #Delimiter = '~'
SET #DelimiterLength = LEN( #Delimiter )
SET #Result = ''
-- Ensures we can terminate loop without using an extra IF condition, saves a few = 0 checks
SET #String = #String + #Delimiter
SET #CurrentPosition = 1
-- Check for next Tilde position
SET #NextDelimiterPosition = CHARINDEX( #Delimiter, #String, #CurrentPosition )
-- Initialise fixed increment position
SET #NextFixedPosition = #Count
-- Compare, which one is earlier and use that one. Handle cases where the next token begins with Tilde
SET #NextPosition = CASE WHEN #NextDelimiterPosition - #DelimiterLength > #NextFixedPosition THEN #NextFixedPosition ELSE #NextDelimiterPosition END
WHILE #NextDelimiterPosition > 0
BEGIN
SET #Result = #Result + SUBSTRING( #String, #CurrentPosition, #NextPosition - #CurrentPosition + 1 )
-- Handle cases where the next token begins with Tilde and avoids doubling up Tildes
+ ( CASE WHEN #NextPosition = #NextDelimiterPosition THEN '' ELSE #Delimiter END )
SET #CurrentPosition = #NextPosition + 1
-- Increment fixed position
SET #NextFixedPosition = #CurrentPosition + #Count - 1
-- Check for next Tilde position
SET #NextDelimiterPosition = CHARINDEX( #Delimiter, #String, #CurrentPosition )
SET #NextPosition = CASE WHEN #NextDelimiterPosition - #DelimiterLength > #NextFixedPosition THEN #NextFixedPosition ELSE #NextDelimiterPosition END
END
-- Remove trailing Tilde
SET #Result = SUBSTRING( #Result, 1, LEN( #Result ) - #DelimiterLength )
RETURN #Result
END
/* Test Cases
SELECT dbo.[AddTilde]( 'ab~c~defghijkl~', 3 ) --> 'ab~c~def~ghi~jkl~'
SELECT dbo.[AddTilde]( '~ab~c~defghijkl', 3 ) --> '~ab~c~def~ghi~jkl'
SELECT dbo.[AddTilde]( 'ab~c~~defghijkl', 3 ) --> 'ab~c~~def~ghi~jkl'
SELECT dbo.[AddTilde]( 'abcdefghijkl', 3 ) --> 'abc~def~ghi~jkl'
SELECT dbo.[AddTilde]( 'a', 3 ) --> 'a'
*/

Remove white spaces from string and convert into title case

Here is the example which i want in output...
I have this input = "Automatic email sent"
But I want this output = "AutomaticEmailSent"
Thanks In Advance!
Use TextInfo.ToTitleCase
// Defines the string with mixed casing.
string myString = "Automatic email sent";
// Creates a TextInfo based on the "en-US" culture.
TextInfo myTI = new CultureInfo("en-US",false).TextInfo;
// Changes a string to titlecase, then replace the spaces with empty
string outputString = myTI.ToTitleCase(myString).Replace(" ", "");
Stealing a function from this answer which takes an text input and make it proper case (otherwise known as title case):
create function ProperCase(#Text as varchar(8000))
returns varchar(8000)
as
begin
declare #Reset bit;
declare #Ret varchar(8000);
declare #i int;
declare #c char(1);
select #Reset = 1, #i=1, #Ret = '';
while (#i <= len(#Text))
select #c= substring(#Text,#i,1),
#Ret = #Ret + case when #Reset=1 then UPPER(#c) else LOWER(#c) end,
#Reset = case when #c like '[a-zA-Z]' then 0 else 1 end,
#i = #i +1
return #Ret
end
Then you can combine this function with REPLACE:
SELECT REPLACE(dbo.ProperCase(column), ' ', '')
FROM MyTable
SQL Server
declare #value varchar(64) = rtrim(' ' + 'Automatic email sent')
;with t(n) as (
select n = charindex(' ', #value, 0)
union all
select n = charindex(' ', #value, n + 1)
from t
where n > 0
)
select #value = stuff(#value, n + 1, 1, upper(substring(#value, n + 1, 1))) from t where n > 0
select replace(#value, ' ', '')

Find a specific substring using Transact-SQL

I need to pull a specific substring from a string of the form:
foo=abc;bar=def;baz=ghi
For example, how would I get the value of "bar" from that string?
You can use charindex and substring. For example, to search for the value of "baz":
declare #str varchar(128)
set #str = 'foo=abc;bar=def;baz=ghi'
-- Make sure #str starts and ends with a ;
set #str = ';' + #str + ';'
select substring(#str,
charindex(';baz=',#str) + len(';baz='),
charindex('=',#str,charindex(';baz=',#str)) - charindex(';baz=',#str) - 1)
Or for the value of "foo" at the start of the string:
select substring(#str,
charindex(';foo=',#str) + len(';foo='),
charindex('=',#str,charindex(';foo=',#str)) - charindex(';foo=',#str) - 1)
Here's a UDF to accomplish this (more readable version inspired by BlackTigerX's answer):
create function dbo.FindValueInString(
#search varchar(256),
#name varchar(30))
returns varchar(30)
as
begin
declare #name_start int
declare #name_length int
declare #value_start int
declare #value_end int
set #search = ';' + #search
set #name_start = charindex(';' + #name + '=',#search)
if #name_start = 0
return NULL
set #name_length = len(';' + #name + '=')
set #value_start = #name_start + #name_length
set #value_end = charindex(';', #search, #value_start)
return substring(#search, #value_start, #value_end - #value_start)
end
As you can see, this isn't easy in Sql Server :) Better do this in the client language, or normalize your database so the substrings go in their own columns.
I have a generalized solution that works for this problem:
CREATE FUNCTION [dbo].[fn_StringBetween]
(
#BaseString varchar(max),
#StringDelim1 varchar(max),
#StringDelim2 varchar(max)
)
RETURNS varchar(max)
AS
BEGIN
DECLARE #at1 int
DECLARE #at2 int
DECLARE #rtrn varchar(max)
SET #at1 = CHARINDEX(#StringDelim1, #BaseString)
IF #at1 > 0
BEGIN
SET #rtrn = SUBSTRING(#BaseString, #at1
+ LEN(#StringDelim1), LEN(#BaseString) - #at1)
SET #at2 = CHARINDEX(#StringDelim2, #rtrn)
IF #at2 > 0
SET #rtrn = LEFT(#rtrn, #at2 - 1)
END
RETURN #rtrn
END
so if you run (just wrap your original string to be searched with ';' at beginning and end):
PRINT dbo.fn_StringBetween(';foo=abc;bar=def;baz=ghi;', ';bar=', ';')
you will get 'def' returned.
look into the PATINDEX function. It has wildcard matching which should help you..
you can use this function
alter function FindValue(#txt varchar(200), #find varchar(200))
returns varchar(200)
as
begin
declare
#firstPos int,
#lastPos int
select #firstPos = charindex(#find, #txt), #lastPos = charindex(';', #txt, #firstPos+5)
select #lastPos = len(#txt)+1 where #lastPos = 0
return substring(#txt, #firstPos+len(#find)+1, #lastPos-#firstPos-len(#find)-1)
end
select dbo.FindValue('foo=abc;bar=def;baz=ghi', 'bar')
update: was not using the length of #find
this is assuming that the string will have the same string format just substitute the column name for the 'foo=abc;bar=def;baz=ghi'
select substring('foo=abc;bar=def;baz=ghi',patindex('%bar=%','foo=abc;bar=def;baz=ghi')+4, len('foo=abc;bar=def;baz=ghi')-patindex('%;baz=%','foo=abc;bar=def;baz=ghi')-4)
This can achieve in simple way
DECLARE #str VARCHAR(30)
DECLARE #start INT
SET #str='foo=abc;bar=def;baz=ghi'
SET #start=CHARINDEX('bar',#str)
PRINT SUBSTRING(#str,#start,3)

SQL Server 2005:charindex starting from the end

I have a string 'some.file.name',I want to grab 'some.file'.
To do that,I need to find the last occurrence of '.' in a string.
My solution is :
declare #someStr varchar(20)
declare #reversedStr varchar(20)
declare #index int
set #someStr = '001.002.003'
set #reversedStr = reverse(#someStr)
set #index = len(#someStr) - charindex('.',#reversedStr)
select left(#someStr,#index)
Well,isn't it too complicated?I was just intented to using 'some.file' in a where-clause.
Anyone has a good idea?
What do you need to do with it?? Do you need to grab the characters after the last occurence of a given delimiter?
If so: reverse the string and search using the normal CHARINDEX:
declare #test varchar(100)
set #test = 'some.file.name'
declare #reversed varchar(100)
set #reversed = REVERSE(#test)
select
REVERSE(SUBSTRING(#reversed, CHARINDEX('.', #reversed)+1, 100))
You'll get back "some.file" - the characters up to the last "." in the original file name.
There's no "LASTCHARINDEX" or anything like that in SQL Server directly. What you might consider doing in SQL Server 2005 and up is great a .NET extension library and deploy it as an assembly into SQL Server - T-SQL is not very strong with string manipulation, whereas .NET really is.
A very simple way is:
SELECT
RIGHT(#str, CHARINDEX('.', REVERSE(#str)) - 1)
This will also work:
DECLARE
#test VARCHAR(100)
SET #test = 'some.file.name'
SELECT
LEFT(#test, LEN(#test) - CHARINDEX('.', REVERSE(#test)))
Take one ')'
declare #test varchar(100)
set #test = 'some.file.name'
select left(#test,charindex('.',#test)+charindex('.',#test)-1)
CREATE FUNCTION [dbo].[Instr] (
-------------------------------------------------------------------------------------------------
-- Name: [dbo].[Instr]
-- Purpose: Find The Nth Value Within A String
-------------------------------------------------------------------------------------------------
-- Revisions:
-- 25-FEB-2011 - HESSR - Initial Revision
-------------------------------------------------------------------------------------------------
-- Parameters:
-- 1) #in_FindString - NVARCHAR(MAX) - INPUT - Input Find String
-- 2) #in_String - NVARCHAR(MAX) - INPUT - Input String
-- 3) #in_StartPos - SMALLINT - INPUT - Position In The String To Start Looking From
-- (If Start Position Is Negative, Search Begins At The End Of The String)
-- (Negative 1 Starts At End Position 1, Negative 3 Starts At End Position Minus 2)
-- 4) #in_Nth - SMALLINT - INPUT - Nth Occurrence To Find The Location For
-------------------------------------------------------------------------------------------------
-- Returns: SMALLINT - Position Of String Segment (Not Found = 0)
-------------------------------------------------------------------------------------------------
#in_FindString NVARCHAR(MAX),
#in_String NVARCHAR(MAX),
#in_StartPos SMALLINT = NULL,
#in_Nth SMALLINT = NULL
)
RETURNS SMALLINT
AS
BEGIN
DECLARE #loc_FindString NVARCHAR(MAX);
DECLARE #loc_String NVARCHAR(MAX);
DECLARE #loc_Position SMALLINT;
DECLARE #loc_StartPos SMALLINT;
DECLARE #loc_Nth SMALLINT;
DECLARE #loc_Idx SMALLINT;
DECLARE #loc_FindLength SMALLINT;
DECLARE #loc_Length SMALLINT;
SET #loc_FindString = #in_FindString;
SET #loc_String = #in_String;
SET #loc_Nth = ISNULL(ABS(#in_Nth), 1);
SET #loc_FindLength = LEN(#loc_FindString+N'.') - 1;
SET #loc_Length = LEN(#loc_String+N'.') - 1;
SET #loc_StartPos = ISNULL(#in_StartPos, 1);
SET #loc_Idx = 0;
IF (#loc_StartPos = ABS(#loc_StartPos))
BEGIN
WHILE (#loc_Idx < #loc_Nth)
BEGIN
SET #loc_Position = CHARINDEX(#loc_FindString,#loc_String,#loc_StartPos);
IF (#loc_Position > 0)
SET #loc_StartPos = #loc_Position + #loc_FindLength
ELSE
SET #loc_Idx = #loc_Nth;
SET #loc_Idx = #loc_Idx + 1;
END;
END
ELSE
BEGIN
SET #loc_StartPos = ABS(#loc_StartPos);
SET #loc_FindString = REVERSE(#in_FindString);
SET #loc_String = REVERSE(#in_String);
WHILE (#loc_Idx < #loc_Nth)
BEGIN
SET #loc_Position = CHARINDEX(#loc_FindString,#loc_String,#loc_StartPos);
IF (#loc_Position > 0)
SET #loc_StartPos = #loc_Position + #loc_FindLength
ELSE
SET #loc_Idx = #loc_Nth;
SET #loc_Idx = #loc_Idx + 1;
END;
IF (#loc_Position > 0)
SET #loc_Position = #loc_Length - #loc_Position + (1 - #loc_FindLength) + 1;
END;
RETURN (#loc_Position);
END;
GO
Here is a shorter version
DECLARE #someStr varchar(20)
set #someStr = '001.002.003'
SELECT REVERSE(Substring(REVERSE(#someStr),CHARINDEX('.', REVERSE(#someStr))+1,20))