使用T-SQL查找最后一次出现子string的索引

有没有一个简单的方法来find最后一次使用SQL的string的索引? 我现在正在使用SQL Server 2000。 我基本上需要.NET System.String.LastIndexOf方法提供的function。 有一点谷歌search显示这一点 – 函数来检索最后的索引 – 但如果你传递一个“文本”列expression式不起作用。 其他解决scheme的其他解决scheme只有在您search的文本长度为1个字符时才起作用。

我可能不得不做一个function。 如果我这样做,我会在这里张贴,所以你们可以看看,也许可以利用。

您仅限于文本数据types的小函数列表 。

所有我能build议的是从PATINDEX开始,但是从DATALENGTH-1, DATALENGTH-2, DATALENGTH-3等倒退,直到得到结果或最终为零(DATALENGTH-DATALENGTH)

这确实是SQL Server 2000根本无法处理的东西。

编辑其他答案 :REVERSE不在SQL Server 2000中可用于文本数据的函数列表中

直截了当的方式? 不,但我用了相反的方式。 从字面上看。

在之前的例程中,为了find给定string的最后一次出现,我使用了REVERSE()函数,接着是CHARINDEX,然后再次使用REVERSE来恢复原始的顺序。 例如:

 SELECT mf.name ,mf.physical_name ,reverse(left(reverse(physical_name), charindex('\', reverse(physical_name)) -1)) from sys.master_files mf 

展示了如何从其“物理名称”中提取实际的数据库文件名,而不pipe子文件夹中的嵌套深度如何。 这只search只有一个字符(反斜杠),但你可以build立在这个更长的searchstring。

唯一的缺点是,我不知道这将如何在TEXT数据types上工作。 我已经在SQL 2005几年了,不再熟悉TEXT的工作 – 但我似乎还记得你可以使用左和右?

菲利普

最简单的方法是…

 REVERSE(SUBSTRING(REVERSE([field]),0,CHARINDEX('[expr]',REVERSE([field])))) 

如果您使用的是Sqlserver 2005或更高版本,多次使用REVERSE函数对性能是不利的,下面的代码更有效率。

 DECLARE @FilePath VARCHAR(50) = 'My\Super\Long\String\With\Long\Words' DECLARE @FindChar VARCHAR(1) = '\' -- Shows text before last slash SELECT LEFT(@FilePath, LEN(@FilePath) - CHARINDEX(@FindChar,REVERSE(@FilePath))) AS Before -- Shows text after last slash SELECT RIGHT(@FilePath, CHARINDEX(@FindChar,REVERSE(@FilePath))-1) AS After -- Shows the position of the last slash SELECT LEN(@FilePath) - CHARINDEX(@FindChar,REVERSE(@FilePath)) AS LastOccuredAt 
 DECLARE @FilePath VARCHAR(50) = 'My\Super\Long\String\With\Long\Words' DECLARE @FindChar VARCHAR(1) = '\' SELECT LEN(@FilePath) - CHARINDEX(@FindChar,REVERSE(@FilePath)) AS LastOccuredAt 

旧的,但仍然有效的问题,所以inheritance人根据他人提供的信息在这里创build。

 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))) 

对我更好

这对我来说工作得很好。

 REVERSE(SUBSTRING(REVERSE([field]), CHARINDEX(REVERSE('[expr]'), REVERSE([field])) + DATALENGTH('[expr]'), DATALENGTH([field]))) 

嗯,我知道这是一个旧的线程,但一个理货表可以在SQL2000(或任何其他数据库)中做到这一点:

 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 

计数表只是一个递增数字的表格。

substring(@str, _Tally.n, 1) = @delim获取每个分隔符的位置,然后您只获得该集合中的最大位置。

理货表很棒。 如果你以前没有用过它们,那么在SQL Server Central上有一篇很好的文章(Free reg,或者只是使用Bug Me Not( http://www.bugmenot.com/view/sqlservercentral.com ))。

*编辑:删除n <= LEN(TEXT_FIELD) ,因为你不能在TEXTtypes上使用LEN()。 只要substring(...) = @delim仍然存在,但结果仍然正确。

反转您的string和您的子string,然后search第一次出现。

我意识到这是一个几年前的问题,但…

Access 2010 ,可以使用InStrRev()来执行此操作。 希望这可以帮助。

我知道这将是低效的,但你有没有考虑将text字段转换为varchar以便您可以使用您find的网站提供的解决scheme? 我知道这个解决scheme会产生问题,因为如果text字段中的长度超出了你的varchar的长度,那么你可能会截断logging(更不用说它不会很高效了)。

由于您的数据位于text字段内(并且您正在使用SQL Server 2000),因此您的选项是有限的。

如果你想得到一个单词串中最后一个空格的索引,你可以使用这个expression式RIGHT(name,(CHARINDEX('',REVERSE(name),0))来返回string中的最后一个单词。如果您想parsing出包含名字首字母和/或中间名字的全名的姓氏,这会很有帮助。

@indexOf = <whatever characters you are searching for in your string>

@LastIndexOf = LEN([MyField]) - CHARINDEX(@indexOf, REVERSE([MyField]))

未经testing,可能由于索引为零而被@indexOf ,但是在从@indexOf字符截断到string结束时在SUBSTRING函数中工作

SUBSTRING([MyField], 0, @LastIndexOf)

我需要在文件夹path中find反斜杠的第n个位置。 这是我的解决scheme。

 /* 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 

这是我的testing用例

 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 

在最后一次出现分隔符之前获取部分(由于DATALENGTH用法,仅适用于NVARCHAR ):

 DECLARE @Fullstring NVARCHAR(30) = '12.345.67890.ABC'; DECLARE @Delimiter CHAR(1) = '.'; SELECT SUBSTRING(@Fullstring, 1, DATALENGTH(@Fullstring)/2 - CHARINDEX(@Delimiter, REVERSE(@Fullstring))); 

一些其他的答案返回一个实际的string,而我更需要知道实际的索引int。 而这样做的答案似乎过分复杂的事情。 用一些其他的答案作为灵感,我做了以下…

首先,我创build了一个函数:

 CREATE FUNCTION [dbo].[LastIndexOf] (@stringToFind varchar(max), @stringToSearch varchar(max)) RETURNS INT AS BEGIN RETURN (LEN(@stringToSearch) - CHARINDEX(@stringToFind,REVERSE(@stringToSearch))) + 1 END GO 

然后,在你的查询中,你可以简单地这样做:

 declare @stringToSearch varchar(max) = 'SomeText: SomeMoreText: SomeLastText' select dbo.LastIndexOf(':', @stringToSearch) 

上面应该返回23(':'的最后一个索引)

希望这对某人来说更容易一些!

这个答案符合OP的要求。 特别是它可以使针不仅仅是一个字符,而且在干草堆里找不到针时也不会产生错误。 在我看来,大多数(所有?)其他答案都没有处理这些边缘案例。 除此之外,我添加了本机MS SQL服务器CharIndex函数提供的“Starting Position”参数。 我试图完全反映CharIndex的规范,除了处理从右到左,而不是从左到右。 例如,如果针或干草堆为空,则返回null,如果在干草堆中找不到针,则返回零。 有一件事我不能解决的是,内置函数的第三个参数是可选的。 使用SQL Server用户定义的函数,除非使用“EXEC”调用函数,否则必须在调用中提供所有参数。 虽然第三个参数必须包含在参数列表中,但您可以提供关键字“default”作为占位符,而不必给它一个值(请参阅下面的示例)。 由于从这个函数中删除第三个参数是比较容易的,如果不需要的话,可以添加它,如果需要的话,我已经把它作为一个起点。

 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 

我遇到这个线程,同时寻找解决scheme,我的类似的问题,有完全相同的要求,但是是一个不同types的数据库,也缺乏REVERSEfunction。

在我的情况下,这是一个OpenEdge(Progress)数据库,它有一个稍微不同的语法。 这使得大多数Oracletypes的数据库提供的INSTRfunction对我可用。

所以我想出了以下代码:

 SELECT INSTR(foo.filepath, '/',1, LENGTH(foo.filepath) - LENGTH( REPLACE( foo.filepath, '/', ''))) AS IndexOfLastSlash FROM foo 

但是,对于我的具体情况(作为OpenEdge(Progress)数据库),这不会导致所需的行为,因为用空字符replace字符的长度与原始string相同。 这对我来说没有什么意义,但我可以用下面的代码绕过这个问题:

 SELECT INSTR(foo.filepath, '/',1, LENGTH( REPLACE( foo.filepath, '/', 'XX')) - LENGTH(foo.filepath)) AS IndexOfLastSlash FROM foo 

现在我明白了,这段代码不会解决T-SQL的问题,因为除了提供Occurence属性的INSTR函数之外,没有其他select。

为了彻底,我将添加创build这个标量函数所需的代码,以便像我在上面的例子中一样使用它。

  -- 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 

为了避免显而易见的情况,当REVERSE函数可用时,你不需要创build这个标量函数,你可以像这样得到所需的结果:

 SELECT LEN(foo.filepath) - CHARINDEX('/', REVERSE(foo.filepath))+1 AS LastIndexOfSlash FROM foo