web-dev-qa-db-ja.com

T-SQLを使用して、サブストリングの最後の出現のインデックスを検索

SQLを使用して文字列の最後の出現のインデックスを見つける簡単な方法はありますか?現在、SQL Server 2000を使用しています。基本的に、.NET System.String.LastIndexOfメソッドが提供する機能が必要です。少しグーグルでこれが明らかになりました- 最後のインデックスを取得する関数 -しかし、「テキスト」列式を渡した場合は機能しません。他のソリューションは、検索するテキストが1文字の長さである場合にのみ機能します。

おそらく関数を作成する必要があります。もしそうするなら、ここに投稿して、皆さんがそれを見て、おそらく利用できるようにします。

110
Raj

テキストデータ型の場合、 関数の小さなリスト に制限されます。

私が提案できるのは、PATINDEXで始まることですが、結果が得られるか、ゼロになるまでDATALENGTH-1, DATALENGTH-2, DATALENGTH-3などから逆方向に動作します(DATALENGTH-DATALENGTH)

これは本当にSQL Server 2000が単に処理できないものです。

他の回答の編集:REVERSEは、SQL Server 2000のテキストデータで使用できる関数のリストに含まれていません

28
gbn

簡単な方法ですか?いいえ、しかし、私は逆を使用しました。文字通り。

以前のルーチンでは、指定された文字列の最後の出現を見つけるために、REVERSE()関数を使用し、CHARINDEXを使用し、その後再びREVERSEを使用して元の順序を復元しました。例えば:

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

サブフォルダー内のネストの深さに関係なく、「物理名」から実際のデータベースファイル名を抽出する方法を示します。これは1文字(バックスラッシュ)のみを検索しますが、より長い検索文字列のためにこれを構築できます。

唯一の欠点は、これがTEXTデータ型でどの程度うまく機能するかわかりません。私は数年前からSQL 2005を使用しており、TEXTの操作に精通していませんが、左と右を使用できることを思い出すようです。

フィリップ

162
Philip Kelley

最も簡単な方法は....

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

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
49
Binoj Antony
DECLARE @FilePath VARCHAR(50) = 'My\Super\Long\String\With\Long\Words'
DECLARE @FindChar VARCHAR(1) = '\'

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

これは私にとって非常にうまくいきました。

REVERSE(SUBSTRING(REVERSE([field]), CHARINDEX(REVERSE('[expr]'), REVERSE([field])) + DATALENGTH('[expr]'), DATALENGTH([field])))
7
Karthik D V

古いがまだ有効な質問なので、ここで他の人から提供された情報に基づいて作成したものをここに示します。

create function fnLastIndexOf(@text varChar(max),@char varchar(1))
returns int
as
begin
return len(@text) - charindex(@char, reverse(@text)) -1
end
7
john
REVERSE(SUBSTRING(REVERSE(ap_description),CHARINDEX('.',REVERSE(ap_description)),len(ap_description)))  

私のためによく働いた

6
mark brito

うーん、これは古いスレッドですが、集計テーブルは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 ))。

*編集:TEXTタイプでLEN()を使用できないため、n <= LEN(TEXT_FIELD)を削除しました。結果がまだ正しいにもかかわらず、substring(...) = @delimが残っている限り。

4
Chris

文字列と部分文字列の両方を逆にして、最初の出現を検索します。

2
A-K

これは数年前の質問ですが、...

Access 2010では、InStrRev()を使用してこれを行うことができます。お役に立てれば。

2
Dan

他の回答のいくつかは実際の文字列を返しますが、実際のインデックス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(「:」の最後のインデックス)を返すはずです

これが誰かにとって少し楽になることを願っています!

1
Matt Goodwin

この回答ではMS SQL Server 2008を使用しています(MS SQL Server 2000にアクセスできません)が、OPによると、3つの状況を考慮する必要があります。私がここで答えていないことを試みたから、それらの3つすべてをカバーしています:

  1. 指定された文字列の検索文字の最後のインデックスを返します。
  2. 指定された文字列の検索部分文字列(1文字以上)の最後のインデックスを返します。
  3. 検索文字または部分文字列が指定された文字列にない場合は、0を返します

私が思いついた関数は2つのパラメーターを取ります。

@String NVARCHAR(MAX):検索される文字列

@FindString NVARCHAR(MAX)@Stringの最後のインデックスを取得する単一の文字またはサブストリング

@FindString@Stringの正のインデックスであるINTまたは0@FindStringにないことを意味する@Stringを返します

関数の機能の説明は次のとおりです。

  1. @ReturnVal0に初期化し、@FindString@Stringにないことを示します
  2. CHARINDEX()を使用して、@FindString@Stringのインデックスを確認します
  3. @FindString@Stringのインデックスが0の場合、@ReturnVal0のままになります
  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を使用しているため)、オプションは制限されます。

1
Andrew Hare

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

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

テストしていません。インデックスが0であるために1つずれている可能性がありますが、@indexOf文字から文字列の終わりまで切り取るときにSUBSTRING関数で機能します

SUBSTRING([MyField], 0, @LastIndexOf)

1
Roan

この回答は、OPの要件を満たしています。具体的には、針を1文字以上にすることができ、haystackで針が見つからない場合でもエラーは生成されません。他の回答のほとんど(すべて?)がこれらのEdgeのケースを処理しなかったように思えました。さらに、ネイティブMS SQLサーバーのCharIndex関数によって提供される「開始位置」引数を追加しました。左から右ではなく、右から左に処理することを除いて、CharIndexの仕様を正確に反映しようとしました。たとえば、needleまたはhaystackがnullの場合はnullを返し、haystackでneedleが見つからない場合は0を返します。回避できなかったことの1つは、組み込み関数では3番目のパラメーターがオプションであることです。 SQL Serverのユーザー定義関数では、関数が "EXEC"を使用して呼び出されない限り、すべてのパラメーターを呼び出しで提供する必要があります。 3番目のパラメーターはパラメーターリストに含める必要がありますが、値を指定しなくても、キーワード「default」をプレースホルダーとして指定できます(以下の例を参照)。必要でない場合に追加するよりも、この関数から3番目のパラメーターを削除する方が簡単なため、ここに開始点として含めました。

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

区切り文字が最後に出現する前の部分を取得するには(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
Hans M

このコードは、部分文字列に複数の文字が含まれている場合でも機能します。

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

まったく同じ要件を持っているが、REVERSE関数も欠けている別の種類のデータベースのための、同様の問題の解決策を探しているときに、このスレッドに出会いました。

私の場合、これはOpenEdge(Progress)データベースの場合で、構文が少し異なります。これにより、INSTR関数が利用可能になりました ほとんどのOracle型付きデータベースが提供

そこで、次のコードを思い付きました。

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の問題を解決しないことを理解しています。これは、INSTRプロパティを提供する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
0
Oceans

フォルダパス内のバックスラッシュの最後から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
fiat

単語の文字列の最後のスペースのインデックスを取得する場合、この式RIGHT(name、(CHARINDEX( ''、REVERSE(name)、0))を使用して、文字列の最後の単語を返すことができます。名またはミドルネームのイニシャルを含むフルネームのラストネームを解析する場合に役立ちます。

0
Justin Stephens