it-swarm.com.de

Finden Sie den Index des letzten Vorkommens einer Unterzeichenfolge mit T-SQL

Gibt es eine einfache Möglichkeit, den Index des letzten Vorkommens einer Zeichenfolge mithilfe von SQL zu finden? Ich verwende momentan SQL Server 2000. Grundsätzlich benötige ich die Funktionalität der .NET System.String.LastIndexOf-Methode. Ein wenig googeln enthüllte diese - Funktion, um den letzten Index abzurufen - aber das funktioniert nicht, wenn Sie einen "Text" -Spaltenausdruck übergeben. Andere Lösungen, die Sie an anderer Stelle finden, funktionieren nur, wenn der gesuchte Text 1 Zeichen lang ist.

Ich werde wahrscheinlich eine Funktion aufkochen müssen. Wenn ich das tue, werde ich es hier posten, damit Sie es sich ansehen und vielleicht nutzen können.

106
Raj

Sie sind auf kleine Funktionsliste für Textdatentyp beschränkt.

Alles, was ich vorschlagen kann, ist mit PATINDEX zu beginnen, aber von DATALENGTH-1, DATALENGTH-2, DATALENGTH-3 usw. rückwärts zu arbeiten, bis Sie ein Ergebnis erhalten oder bei null enden (DATALENGTH-DATALENGTH).

Das ist wirklich etwas, mit dem SQL Server 2000 einfach nicht umgehen kann.

Bearbeiten für andere Antworten: REVERSE ist nicht in der Liste der Funktionen enthalten, die mit Textdaten in SQL Server 2000 verwendet werden können

27
gbn

Einfacher Weg? Nein, aber ich habe das Gegenteil benutzt. Buchstäblich.

Um das letzte Vorkommen einer bestimmten Zeichenfolge zu finden, habe ich in früheren Routinen die Funktion REVERSE () verwendet, gefolgt von CHARINDEX, gefolgt von REVERSE, um die ursprüngliche Reihenfolge wiederherzustellen. Zum Beispiel:

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

zeigt, wie die tatsächlichen Datenbankdateinamen aus ihren "physischen Namen" extrahiert werden, unabhängig davon, wie tief sie in Unterordnern verschachtelt sind. Dies sucht nur nach einem Zeichen (dem Backslash), aber Sie können darauf aufbauen, um längere Suchzeichenfolgen zu erhalten.

Der einzige Nachteil ist, ich weiß nicht, wie gut dies mit TEXT-Datentypen funktioniert. Ich bin seit ein paar Jahren mit SQL 2005 beschäftigt und bin nicht mehr mit der Arbeit mit TEXT vertraut - aber ich erinnere mich daran, dass Sie LINKS und RECHTS dazu verwenden könnten?

Philip

159
Philip Kelley

Der einfachste Weg ist ....

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

Wenn Sie Sqlserver 2005 oder höher verwenden, wirkt sich die Verwendung der REVERSE-Funktion oft nachteilig auf die Leistung aus. Der folgende Code ist effizienter.

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

Das hat sehr gut für mich funktioniert.

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

funktionierte besser für mich

6
mark brito

Alte, aber immer noch gültige Frage, also heres, was ich basierend auf den von anderen hier bereitgestellten Informationen erstellt habe.

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

Hmm, ich weiß, dass dies ein alter Thread ist, aber eine Zählungstabelle könnte dies in SQL2000 (oder einer anderen Datenbank) tun:

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

Eine Tabelle ist nur eine Tabelle mit steigenden Zahlen.

substring(@str, _Tally.n, 1) = @delim erhält die Position jedes Begrenzungszeichens. Dann wird nur die maximale Position in diesem Satz angezeigt.

Tally-Tische sind großartig. Wenn Sie sie noch nicht verwendet haben, finden Sie unter SQL Server Central einen guten Artikel (Free reg, oder verwenden Sie einfach Bug Me Not ( http://www.bugmenot.com/view/sqlservercentral.com) )).

* EDIT: n <= LEN(TEXT_FIELD) wurde entfernt, da Sie LEN () nicht für den TEXT-Typ verwenden können. Solange die substring(...) = @delim bleibt, ist das Ergebnis immer noch korrekt.

4
Chris

Kehren Sie Ihren String und Ihren Teilstring um und suchen Sie dann nach dem ersten Vorkommen.

2
A-K

Mir ist klar, dass dies eine mehrere Jahre alte Frage ist, aber ...

Bei Access 2010 können Sie InStrRev() verwenden. Hoffe das hilft.

2
Dan

Einige der anderen Antworten geben eine tatsächliche Zeichenfolge zurück, während ich mehr über den tatsächlichen Index int wissen musste. Und die Antworten, die das tun, scheinen die Dinge überkompliziert zu machen. Ich verwendete einige der anderen Antworten als Inspiration und tat Folgendes:.

Zuerst habe ich eine Funktion erstellt:

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

Dann können Sie in Ihrer Abfrage einfach Folgendes tun:

declare @stringToSearch varchar(max) = 'SomeText: SomeMoreText: SomeLastText'

select dbo.LastIndexOf(':', @stringToSearch)

Das obige sollte 23 zurückgeben (der letzte Index von ':')

Hoffe, das hat es für jemanden etwas einfacher gemacht!

1
Matt Goodwin

Diese Antwort verwendet MS SQL Server 2008 (ich habe keinen Zugriff auf MS SQL Server 2000), aber die Art, wie ich es gemäß dem OP sehe, sind 3 Situationen, die zu berücksichtigen sind. Von dem, was ich hier nicht ausprobiert habe, werden alle drei von ihnen behandelt:

  1. Gibt den letzten Index eines Suchzeichens in einer angegebenen Zeichenfolge zurück.
  2. Gibt den letzten Index einer Such-Teilzeichenfolge (mehr als nur ein einzelnes Zeichen ) In einer bestimmten Zeichenfolge zurück.
  3. Wenn das Suchzeichen oder die Unterzeichenfolge nicht in der angegebenen Zeichenfolge enthalten ist, geben Sie 0 zurück.

Die Funktion, die ich mir ausgedacht habe, hat zwei Parameter:

@String NVARCHAR(MAX): Die zu durchsuchende Zeichenfolge

@FindString NVARCHAR(MAX): Entweder ein einzelnes Zeichen oder eine Unterzeichenfolge, um das letzte .__ zu erhalten. Index von in @String

Es gibt eine INT zurück, die entweder der positive Index von @FindString in @String oder 0 ist, was bedeutet, dass @FindString nicht in @String ist.

Hier ist eine Erklärung, was die Funktion bewirkt:

  1. Initialisiert @ReturnVal auf 0 und gibt an, dass @FindString nicht in @String ist.
  2. Überprüft den Index des @FindString in @String mithilfe von CHARINDEX() 
  3. Wenn der Index von @FindString in @String0 ist, wird @ReturnVal als 0 belassen.
  4. Wenn der Index von @FindString in @String> 0 ist, befindet sich @FindString in @String __. Er berechnet den letzten Index von @FindString in @String mithilfe von REVERSE().
  5. Gibt @ReturnVal zurück, entweder eine positive Zahl, die der letzte Index von @FindString in @String ist, oder 0, die angibt, dass @FindString nicht in @String liegt.

Hier ist das Skript zum Erstellen von Funktionen (Kopieren und Einfügen fertig):

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

Hier ist ein bisschen, das die Funktion bequem testet:

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)

Ich habe dies nur unter MS SQL Server 2008 ausgeführt, weil ich keinen Zugriff auf eine andere Version habe, aber von dem, was ich mir angeschaut habe, sollte dies zumindest für 2008+ gut sein.

Genießen.

1

Ich weiß, dass dies ineffizient sein wird, aber haben Sie in Betracht gezogen, das Feld text in varchar umzuwandeln, damit Sie die von der gefundenen Website bereitgestellte Lösung verwenden können? Ich weiß, dass diese Lösung Probleme verursachen würde, da Sie möglicherweise den Datensatz abschneiden könnten, wenn die Länge im Feld text die Länge Ihrer varchar überschreitet (ganz zu schweigen davon, dass dies nicht sehr performant wäre).

Da sich Ihre Daten in einem text-Feld befinden (und Sie SQL Server 2000 verwenden), sind Ihre Optionen begrenzt.

1
Andrew Hare

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

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

Wurde nicht getestet, ist es möglicherweise aufgrund des Nullindexes um eins abgeschaltet, funktioniert jedoch in der Funktion SUBSTRING, wenn von @indexOf Zeichen bis zum Ende der Zeichenfolge abgeschnitten wird

SUBSTRING([MyField], 0, @LastIndexOf)

1
Roan

Diese Antwort entspricht den Anforderungen des OP. Insbesondere kann die Nadel mehr als ein einzelnes Zeichen sein und es wird kein Fehler generiert, wenn die Nadel nicht im Heuhaufen gefunden wird. Es schien mir, dass die meisten (alle?) Der anderen Antworten diese Edge-Fälle nicht behandelten. Darüber hinaus fügte ich das Argument "Startposition" hinzu, das von der nativen MS SQL Server-CharIndex-Funktion bereitgestellt wird. Ich habe versucht, die Spezifikation für CharIndex genau zu spiegeln, außer von rechts nach links statt von links nach rechts. Ich gebe beispielsweise Null zurück, wenn entweder Nadel oder Heuhaufen Null ist, und ich gebe Null zurück, wenn die Nadel nicht im Heuhaufen gefunden wird. Eine Sache, die ich nicht umgehen konnte ist, dass der dritte Parameter mit der eingebauten Funktion optional ist. Bei benutzerdefinierten Funktionen von SQL Server müssen alle Parameter im Aufruf angegeben werden, es sei denn, die Funktion wird mit "EXEC" aufgerufen. Während der dritte Parameter in der Parameterliste enthalten sein muss, können Sie das Schlüsselwort "default" als Platzhalter dafür angeben, ohne einen Wert angeben zu müssen (siehe Beispiele unten). Da es einfacher ist, den dritten Parameter aus dieser Funktion zu entfernen, wenn dies nicht gewünscht ist, als hinzuzufügen, wenn nötig, habe ich ihn hier als Ausgangspunkt hinzugefügt.

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

Um das Teil vor dem letzten Auftreten des Trennzeichens zu erhalten (funktioniert nur für NVARCHAR aufgrund der Verwendung von 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

Dieser Code funktioniert auch, wenn der Teilstring mehr als 1 Zeichen enthält.

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

Ich bin auf diesen Thread gestoßen, als ich nach einer Lösung für mein ähnliches Problem gesucht habe, die genau dieselbe Anforderung hatte, aber für eine andere Art von Datenbank war, der auch die REVERSE-Funktion fehlte.

In meinem Fall war dies eine OpenEdge (Progress) Datenbank, die eine etwas andere Syntax hat. Dies machte die INSTR-Funktion für mich verfügbar, die die meisten Oracle-typisierten Datenbanken anbieten.

Also kam mir folgender Code:

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

In meiner speziellen Situation (die Datenbank OpenEdge (Progress)) führte dies jedoch nicht zu dem gewünschten Verhalten, da das Ersetzen des Zeichens durch ein leeres Zeichen dieselbe Länge wie die ursprüngliche Zeichenfolge ergab. Das macht für mich nicht viel Sinn, aber ich konnte das Problem mit dem folgenden Code umgehen:

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

Jetzt verstehe ich, dass dieser Code das Problem für T-SQL nicht lösen kann, da es keine Alternative zur INSTR-Funktion gibt, die die Occurence-Eigenschaft anbietet.

Um gründlich zu sein, füge ich den Code hinzu, der zum Erstellen dieser Skalarfunktion benötigt wird, damit er auf die gleiche Weise verwendet werden kann wie in den obigen Beispielen.

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

Um das Offensichtliche zu vermeiden, müssen Sie, wenn die Funktion REVERSE verfügbar ist, diese Skalarfunktion nicht erstellen. Sie können das erforderliche Ergebnis wie folgt erhalten:

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

Ich musste die n-te letzte Position eines Backslash in einem Ordnerpfad finden. Hier ist meine Lösung.

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

Hier sind meine Testfälle, die bestehen

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

Wenn Sie den Index des letzten Leerzeichens in einer Folge von Wörtern abrufen möchten, können Sie diesen Ausdruck RIGHT (Name, (CHARINDEX ('', REVERSE (Name), 0)) verwenden, um das letzte Wort in der Zeichenfolge zurückzugeben string. Dies ist hilfreich, wenn Sie den Nachnamen eines vollständigen Namens heraussuchen möchten, der Initialen für den ersten und/oder den zweiten Vornamen enthält.

0
Justin Stephens