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

时间:2009-06-21 23:10:41

标签: sql-server string tsql search sql-server-2000

是否有一种直接的方法可以使用SQL查找最后一次出现的字符串的索引?我现在正在使用SQL Server 2000。我基本上需要.NET System.String.LastIndexOf方法提供的功能。一个小小的谷歌搜索显示了这个 - Function To Retrieve Last Index - 但如果你传入一个“文本”列表达式,这不起作用。在其他地方找到的其他解决方案只有在您搜索的文本长度为1个字符时才起作用。

我可能要做一个功能。如果我这样做,我会在这里发布,所以你们大家可以看看它,也许可以使用它。

22 个答案:

答案 0 :(得分:161)

直截了当的方式?不,但我已经反过来了。字面上。

在先前的例程中,为了找到给定字符串的最后一次出现,我使用了REVERSE()函数,然后是CHARINDEX,再次使用REVERSE来恢复原始顺序。例如:

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

显示了如何从“物理名称”中提取实际数据库文件名,无论嵌套在子文件夹中有多深。这只会搜索一个字符(反斜杠),但你可以在此基础上搜索更长的搜索字符串。

唯一的缺点是,我不知道这对TEXT数据类型有多好。我已经在SQL 2005上工作了几年,而且不再熟悉使用TEXT了 - 但我似乎记得你可以使用LEFT和RIGHT吗?

菲利普

答案 1 :(得分:95)

最简单的方法是......

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

答案 2 :(得分:48)

如果您使用的是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

答案 3 :(得分:28)

您的文字数据类型仅限small list of functions

我所能建议的只是从PATINDEX开始,但是从DATALENGTH-1, DATALENGTH-2, DATALENGTH-3等向后工作直到你得到一个结果或最终为零(DATALENGTH-DATALENGTH)

这确实是SQL Server 2000根本无法处理的事情。

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

答案 4 :(得分:14)

DECLARE @FilePath VARCHAR(50) = 'My\Super\Long\String\With\Long\Words'
DECLARE @FindChar VARCHAR(1) = '\'

SELECT LEN(@FilePath) - CHARINDEX(@FindChar,REVERSE(@FilePath)) AS LastOccuredAt

答案 5 :(得分:7)

这对我很有用。

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

答案 6 :(得分:6)

旧的但仍然有效的问题,所以我根据其他人提供的信息创建了我所创建的内容。

create function fnLastIndexOf(@text varChar(max),@char varchar(1))
returns int
as
begin
return len(@text) - charindex(@char, reverse(@text)) -1
end

答案 7 :(得分:6)

REVERSE(SUBSTRING(REVERSE(ap_description),CHARINDEX('.',REVERSE(ap_description)),len(ap_description)))  

对我来说效果更好

答案 8 :(得分:4)

嗯,我知道这是一个旧线程,但是一个计数表可以在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获取每个分隔符的位置,然后您只需获得该集合中的最大位置。

Tally表非常棒。如果您之前没有使用它们,那么SQL Server Central上有一篇很好的文章(免费注册,或者只使用Bug Me Not(http://www.bugmenot.com/view/sqlservercentral.com))。

*编辑:已移除n <= LEN(TEXT_FIELD),因为您无法在TEXT类型上使用LEN()。只要substring(...) = @delim仍然存在,但结果仍然正确。

答案 9 :(得分:2)

反转你的字符串和你的子字符串,然后搜索第一次出现。

答案 10 :(得分:2)

我意识到这是一个有几年历史的问题,但是......

Access 2010上,您可以使用InStrRev()执行此操作。希望这会有所帮助。

答案 11 :(得分:1)

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

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

没有经过测试,由于索引为零,它可能会被一个关闭,但是当从SUBSTRING字符切换到字符串末尾时,它可以在@indexOf函数中起作用

SUBSTRING([MyField], 0, @LastIndexOf)

答案 12 :(得分:1)

此答案使用MS SQL Server 2008(我没有访问MS SQL Server 2000的权限),但是根据OP的观察方式,有3种情况需要考虑。根据我没有尝试的答案,这里涵盖了所有这三个方面:

  1. 返回给定字符串中搜索字符的最后一个索引。
  2. 返回搜索子字符串的最后一个索引(不仅仅是单个索引) 字符)。
  3. 如果搜索字符或子字符串不在给定的字符串中,则返回0

我想出的函数需要两个参数:

@String NVARCHAR(MAX):要搜索的字符串

@FindString NVARCHAR(MAX):使用单个字符或子字符串来获取最后一个                               @String

中的索引

它返回一个INT,它是@FindString@String0的正索引,表示@FindString不在@String

以下是该功能的解释:

  1. @ReturnVal初始化为0,表明@FindString不在@String
  2. 使用@FindString检查@StringCHARINDEX()的索引
  3. 如果@FindString@String的索引是0,则@ReturnVal保留为0
  4. 如果@FindString@String的索引是> 0,则@FindString@String中,因此 它使用@FindString
  5. 计算@StringREVERSE()的最后一个索引
  6. 返回@ReturnVal,它是一个正数,它是的最后一个索引 @FindString@String中的0表示@FindString不在@String

这是创建功能脚本(准备好复制和粘贴):

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

这里有一点可以方便地测试功能:

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)

我只能在MS SQL Server 2008上运行此程序,因为我无权访问任何其他版本,但从我的调查来看,这至少对2008+起了作用。

享受。

答案 13 :(得分:1)

其他一些答案会返回一个实际的字符串,而我更需要知道实际的索引int。这样做的答案似乎过于复杂。使用其他一些答案作为灵感,我做了以下......

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

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(&#39;的最后一个索引:&#39;)

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

答案 14 :(得分:1)

我知道效率低下但是您考虑将text字段转换为varchar,以便您可以使用您找到的网站提供的解决方案吗?我知道这个解决方案会产生问题,因为如果text字段中的长度溢出varchar的长度,你可能会截断记录(更不用说它不会非常高效)。

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

答案 15 :(得分:0)

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

DECLARE @Fullstring NVARCHAR(30) = '12.345.67890.ABC';

DECLARE @Delimiter CHAR(1) = '.';

SELECT SUBSTRING(@Fullstring, 1, DATALENGTH(@Fullstring)/2 - CHARINDEX(@Delimiter, REVERSE(@Fullstring)));

答案 16 :(得分:0)

我需要在文件夹路径中找到反斜杠的第n个最后位置。 这是我的解决方案。

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

以下是我传递的测试用例

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

答案 17 :(得分:0)

这个答案符合OP的要求。特别是它允许针不仅仅是一个字符,并且在大海捞针中找不到针时不会产生错误。在我看来,大多数(所有?)其他答案都没有处理这些边缘情况。除此之外,我添加了&#34;起始位置&#34;由本机MS SQL服务器CharIndex函数提供的参数。我尝试完全镜像CharIndex的规范,除了从右到左而不是从左到右处理。例如,如果needle或haystack为null,则返回null,如果在haystack中找不到needle,则返回零。我无法解决的一件事是,使用内置函数,第三个参数是可选的。对于SQL Server用户定义的函数,除非使用&#34; EXEC&#34;来调用函数,否则必须在调用中提供所有参数。 。虽然第三个参数必须包含在参数列表中,但您可以提供关键字&#34;默认&#34;作为它的占位符而不必给它一个值(见下面的例子)。因为如果不需要,可以更容易地从该函数中删除第三个参数,如果需要的话,可以添加它作为起点。

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

答案 18 :(得分:0)

我在搜索我的类似问题的解决方案时遇到了这个问题,该解决方案具有完全相同的要求但是对于另一种也缺少REVERSE函数的数据库。

在我的情况下,这是针对 OpenEdge(Progress)数据库,其语法略有不同。这使得INSTR函数SELECT INSTR(foo.filepath, '/',1, LENGTH(foo.filepath) - LENGTH( REPLACE( foo.filepath, '/', ''))) AS IndexOfLastSlash FROM foo 可以使用most Oracle typed databases offer

所以我想出了以下代码:

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

但是,对于我的特定情况(作为 OpenEdge(进度)数据库),这不会导致所需的行为,因为用空字符替换字符会给出与原始字符串相同的长度。这对我来说没有多大意义,但我能够通过以下代码绕过问题:

INSTR

现在我明白这段代码无法解决 T-SQL 的问题,因为除了提供Occurence属性的 -- 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

为避免显而易见,当SELECT LEN(foo.filepath) - CHARINDEX('/', REVERSE(foo.filepath))+1 AS LastIndexOfSlash FROM foo 函数可用时,您不需要创建此标量函数,您只需得到所需的结果:

{{1}}

答案 19 :(得分:0)

即使子字符串包含多个字符,此代码也有效。

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

答案 20 :(得分:0)

如果要获取字符串中最后一个空格的索引,可以使用此表达式 RIGHT(name,(CHARINDEX('',REVERSE(name),0))返回字符串中的最后一个单词。如果要解析包含第一个和第一个名称的首字母的全名的姓氏,这将非常有用。 /或中间名。

答案 21 :(得分:0)

处理lookinng大于1个字符的内容。 如果愿意,可以随意增加参数大小。

无法抗拒发布

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 

和测试

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