T-SQL을 사용하여 하위 문자열이 마지막으로 발생한 인덱스 찾기


127

SQL을 사용하여 마지막으로 나타나는 문자열의 색인을 찾는 간단한 방법이 있습니까? 지금 SQL Server 2000을 사용하고 있습니다. 기본적으로 .NET System.String.LastIndexOf메서드가 제공 하는 기능이 필요 합니다. 약간의 인터넷 검색에서 이것을 보여주었습니다- 마지막 색인을 검색하는 기능 - "텍스트"열 표현식을 전달하면 작동하지 않습니다. 다른 곳에서 찾은 다른 솔루션은 검색하는 텍스트가 1 자 길이 인 경우에만 작동합니다.

아마도 기능을 요리해야 할 것입니다. 그렇게하면 여기에 게시하여 여러분이보고 사용하도록하겠습니다.

답변:


33

텍스트 데이터 유형에 대한 작은 기능 목록으로 제한됩니다 .

내가 제안 할 수있는 것은로 시작 PATINDEX하지만 DATALENGTH-1, DATALENGTH-2, DATALENGTH-3결과를 얻거나 0으로 끝날 때까지 등에서 뒤로 작동합니다 (DATALENGTH-DATALENGTH)

이것은 실제로 SQL Server 2000처리 할 수없는 것입니다 .

다른 답변 편집 : REVERSE는 SQL Server 2000의 텍스트 데이터와 함께 사용할 수있는 함수 목록에 없습니다.


1
예, 꽤 어색합니다. 이것은 단순 해야하는 것처럼 보이지만 그렇지 않습니다!
Raj

... 이것이 SQL 2005에 varchar (max)가 정상 기능을 허용하는 이유입니다
gbn

1
아! "varchar (max)"는 SQL 2005로 SQL 2000에서 시도했을 때 왜 작동하지 않는지를 설명합니다.
Raj

LENGTH가 작동하지만 DATALENGTH가 올바른 결과를 얻지 못합니다.
Tequila

@Tequila 및 기타 : DATALENGTH문자가 아닌 바이트 수를 반환합니다. 따라서 DATALENGTH문자열의 문자열에서 2 x 문자 수를 반환합니다 NVARCHAR. LEN그러나 후행 공백을 뺀 문자 수를 반환합니다 . 나는 결코 사용하지 DATALENGTH공백을 후행하는 것은 중요하지 않는 문자 길이 계산과 내 데이터 유형들이이든, 일관성이 있는지 확실히 알고 VARCHARNVARCHAR
rbsdca

174

간단한 방법? 아니요, 그러나 나는 그 반대를 사용했습니다. 말 그대로.

이전의 루틴에서 주어진 문자열의 마지막 발생을 찾기 위해 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 작업에 익숙하지 않지만 왼쪽과 오른쪽을 사용할 수 있다고 생각합니다.

필립


1
죄송합니다. 2000으로 작업 할 때 다시는 돌아 가지 않았으며 현재 SQL 2000 설치에 액세스 할 수 없습니다.
Philip Kelley

훌륭한! 이런 식으로이 문제를 공격한다고 생각하지 않았을 것입니다!
Jared

4
좋은 것! 나는 내 자신의 필요에 맞게 수정 : email.Substring (0, email.lastIndexOf ( '@')) == SELECT LEFT (이메일, LEN (이메일) -CHARINDEX를 ( '@', REVERSE (이메일)))
프레드릭 요한슨

1
이와 같은 영리한 것들이 프로그래밍이 그렇게 재미있는 이유입니다!
크리스

왜 여분의 반전 대신 원본에 왼쪽 대신 오른쪽을 사용하지 않는가
Phil

108

가장 간단한 방법은 ...

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

3
+1 일치하는 항목이 없으면 '잘못된 길이 매개 변수가 LEFT 또는 SUBSTRING 함수에 전달되었습니다'와 같은 NOT 오류가 발생하지 않습니다.
Xilmiki

12
귀하의 경우 [expr]1 개 심볼 이상, 당신은 너무 그것을 반대합니다!
Andrius Naruševičius

60

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

1
후시에는 분명해 보이지만 단일 문자 대신 문자열을 검색하는 경우 LEN (@FilePath)-CHARINDEX (REVERSE (@FindString), REVERSE (@FilePath))
pkExec

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

8

오래되었지만 여전히 유효한 질문이므로 여기에 다른 사람들이 제공 한 정보를 기반으로 만든 내용이 있습니다.

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

7

이것은 나를 위해 아주 잘 작동했습니다.

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

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

나를 위해 더 잘했다


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다음 그냥 세트의 최대 위치를 얻을, 각 단락의 위치를 가져옵니다.

탈리 테이블은 훌륭합니다. 이전에 사용해 본 적이 없다면 SQL Server Central 에 대한 좋은 기사가 있습니다 (무료 등록 또는 Bug Me Not ( http://www.bugmenot.com/view/sqlservercentral.com )).

* 편집 : n <= LEN(TEXT_FIELD)TEXT 유형에 LEN ()을 사용할 수 없으므로 제거 되었습니다. 만큼으로 substring(...) = @delim남아 그 결과는 여전히 정확하지만.


좋은. 나는 이것이 gbn에 의해 받아 들여진 대답과 사실상 동일한 해결책이라고 생각합니다. DATALENGTH에서 빼고 마지막 문자가 아닌 첫 번째 문자부터 읽는 정수 1, 2, 3 등을 저장하기 위해 테이블을 사용하고 있습니다.
Michael Petito

2

문자열과 하위 문자열을 모두 바꾸고 첫 번째 항목을 검색하십시오.


좋은 지적. 나는 지금 2000을 가지고 있지 않으며, 내가 할 때 할 수 있다면 회상 할 수 없습니다.
AK

2

다른 답변 중 일부는 실제 문자열을 반환하지만 실제 인덱스 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을 반환해야합니다 ( ':'의 마지막 색인)

이것이 누군가에게 조금 더 쉬워 졌기를 바랍니다!


2

나는 이것이 몇 년 된 질문이라는 것을 알고 있지만 ...

켜짐 Access 2010, 당신이 사용할 수있는 InStrRev()이 작업을 수행 할 수 있습니다. 도움이 되었기를 바랍니다.


2

이 답변은 MS SQL Server 2008 (MS SQL Server 2000에 액세스 할 수 없음)을 사용하지만 OP에 따라 보는 방식은 3 가지 상황을 고려해야합니다. 내가 시도하지 않은 답변에서 여기에 3 가지 모두가 포함됩니다.

  1. 주어진 문자열에서 검색 문자의 마지막 색인을 반환합니다.
  2. 주어진 문자열에서 검색 하위 문자열 (단일 문자 이상)의 마지막 인덱스를 반환합니다.
  3. 검색 문자 또는 하위 문자열이 주어진 문자열에 없으면 0

내가 생각해 낸 기능에는 두 가지 매개 변수가 있습니다.

@String NVARCHAR(MAX) : 검색 할 문자열

@FindString NVARCHAR(MAX) :의 마지막 색인을 가져올 단일 문자 또는 하위 문자열 @String

in INT의 양의 인덱스 이거나에없는 의미를 반환합니다 .@FindString@String0@FindString@String

함수의 기능에 대한 설명은 다음과 같습니다.

  1. 를 초기화 @ReturnVal하는 0것을 의미는 @FindString아닙니다@String
  2. 수표의 인덱스 @FindString@String사용하여CHARINDEX()
  3. 의 인덱스 경우 @FindString에이 @String되고 0, @ReturnVal로 남겨0
  4. 의 인덱스 경우 @FindString에가 @String있다 > 0, @FindString@String그것의 마지막 인덱스 계산 때문에 @FindString에서를 @String사용하여REVERSE()
  5. 반환 @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+ 이상에 좋을 것입니다.

즐겨.


1

나는 그것이 비효율적이라는 것을 알고 있지만 당신이 찾은 웹 사이트에서 제공하는 솔루션을 사용할 수 있도록 text필드 캐스팅을 고려 varchar했습니까? text필드의 길이가 오버플로 된 경우 레코드가 잘릴 수 있으므로이 솔루션으로 인해 문제가 발생할 수 있음을 알고 있습니다.varchar 있습니다.

데이터가 내부에 있기 때문에 text 필드 SQL Server 2000을 사용하고 옵션이 제한됩니다.


예. 처리되는 데이터가 "varchar"에 보유 할 수있는 최대 값을 초과하는 경우가 많으므로 "varchar"로 캐스트하는 옵션이 아닙니다. 그래도 답변 주셔서 감사합니다!
Raj

1

단어 문자열에서 마지막 공백의 색인을 얻으려면이 표현식 RIGHT (name, (CHARINDEX ( '', REVERSE (name), 0))을 사용하여 문자열의 마지막 단어를 리턴 할 수 있습니다. 이름 및 / 또는 중간 이름의 이니셜이 포함 된 전체 이름의 성을 구문 분석하려는 경우 유용합니다.


1

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

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

테스트를 거치지 않았지만 인덱스가 0이기 때문에 1만큼 꺼져있을 수 있지만 문자에서 문자열 끝까지 SUBSTRING잘릴 때 작동 @indexOf합니다.

SUBSTRING([MyField], 0, @LastIndexOf)


1

이 코드는 하위 문자열에 둘 이상의 문자가 포함 된 경우에도 작동합니다.

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

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

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

0

이 답변은 OP의 요구 사항을 충족합니다. 특히 바늘이 단일 문자 이상이 될 수 있으며 건초 더미에서 바늘을 찾을 수 없을 때 오류가 발생하지 않습니다. 다른 답변의 대부분 (모두?)이 그와 같은 경우를 처리하지 않은 것처럼 보였습니다. 그 외에도 기본 MS SQL 서버 CharIndex 함수가 제공하는 "시작 위치"인수를 추가했습니다. 왼쪽에서 오른쪽 대신 오른쪽에서 왼쪽으로 처리하는 것을 제외하고 CharIndex의 사양을 정확하게 반영하려고했습니다. 예를 들어 needle 또는 haystack이 null이면 null을 반환하고 haystack에 needle이 없으면 0을 반환합니다. 내가 해결할 수없는 한 가지는 내장 함수를 사용하여 세 번째 매개 변수가 선택 사항이라는 것입니다. 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

0

정확히 동일한 요구 사항이 있지만 다른 종류의 데이터베이스에 대한 비슷한 문제에 대한 솔루션을 검색하는 동안이 스레드를 발견했습니다. REVERSE 기능 .

필자의 경우 이것은 약간 다른 구문 을 가진 OpenEdge (Progress) 데이터베이스를위한 것입니다. 이로 인해 대부분의 Oracle 유형 데이터베이스가 제공 하는 INSTR기능을 사용할 수있게되었습니다. .

그래서 다음 코드를 생각해 냈습니다.

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

그러나 특정 상황 ( OpenEdge (Progress) 데이터베이스)의 경우 문자를 빈 문자로 바꾸면 원래 문자열과 길이가 같기 때문에 원하는 동작을 얻지 못했습니다. 이것은 나에게 의미가 없지만 아래 코드의 문제를 무시할 수있었습니다.

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

이제이 코드는 속성 을 제공하는 함수에 대한 대안이 없기 때문에 T-SQL 의 문제를 해결하지 못한다는 것을 알고 있습니다.INSTROccurence

철저히하기 위해이 스칼라 함수를 생성하는 데 필요한 코드를 추가하여 위 예제에서와 같은 방식으로 사용할 수 있습니다.

  -- 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.