Найти индекс последнего вхождения подстроки с помощью T-SQL
есть ли простой способ найти индекс последнего вхождения строки с помощью SQL? Я использую SQL Server 2000 прямо сейчас. Мне в основном нужна функциональность, что .NET System.String.LastIndexOf метод обеспечивает. Немного погуглив показал это - Функция Для Получения Последнего Индекса - но это не работает, если вы передаете выражение столбца "текст". Другие решения, найденные в другом месте, работают только до тех пор, пока текст, который вы ищете, составляет 1 символ длинный.
Мне, вероятно,придется готовить функцию. Если я это сделаю, я опубликую его здесь, чтобы вы могли посмотреть на него и, возможно, использовать.
20 ответов:
вы ограничены небольшой список функций для текстового типа данных.
все, что я могу предложить начать с
PATINDEX, а затемDATALENGTH-1, DATALENGTH-2, DATALENGTH-3etc, пока вы не получите результат или не окажетесь на нуле (DATALENGTH-DATALENGTH)Это действительно то, что
SQL Server 2000просто не могут справиться.редактирование других ответов : REVERSE не входит в список функций, которые можно использовать с текстовыми данными в SQL Server 2000
простым способом? Нет, но я использовал обратное. Буквально.
в предыдущих подпрограммах, чтобы найти последнее вхождение данной строки, я использовал функцию REVERSE (), затем CHARINDEX, а затем снова REVERSE для восстановления исходного порядка. Например:
SELECT mf.name ,mf.physical_name ,reverse(left(reverse(physical_name), charindex('\', reverse(physical_name)) -1)) from sys.master_files mfпоказывает, как извлечь фактические имена файлов базы данных из их "физических имен", независимо от того, насколько глубоко вложены во вложенные папки. Это делает поиск только одного символа (обратная косая черта), но вы можно построить на этом для более длинных строк поиска.
единственным недостатком является то, что я не знаю, насколько хорошо это будет работать с текстовыми типами данных. Я был на SQL 2005 в течение нескольких лет, и больше не знаком с работой с текстом-но я, кажется, помню, что вы могли бы использовать левый и правый на нем?
Филипп
самый простой способ-это....
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
Это очень хорошо для меня.
REVERSE(SUBSTRING(REVERSE([field]), CHARINDEX(REVERSE('[expr]'), REVERSE([field])) + DATALENGTH('[expr]'), DATALENGTH([field])))
старый, но все еще действительный вопрос, так вот что я создал на основе информации, предоставленной другим здесь.
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)))работал лучше для меня
Хм, я знаю, что это старый поток, но таблица подсчета может сделать это в 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таблица подсчета-это просто таблица возрастающих чисел.
The
substring(@str, _Tally.n, 1) = @delimполучает положение каждого разделителя, то вы просто получите максимальную позицию в этом наборе.таблицы подсчета являются удивительными. Если вы не использовали их раньше, есть хорошая статья на SQL Server Central (Free reg, или просто используйте Bug Me Not (http://www.bugmenot.com/view/sqlservercentral.com)).
* EDIT: Removed
n <= LEN(TEXT_FIELD), так как вы не можете использовать LEN() для типа текста. До тех пор, покаsubstring(...) = @delimостается, хотя результат все равно правильный.
Я понимаю, что это вопрос нескольких лет, но...
On
Access 2010, вы можете использоватьInStrRev()для этого. Надеюсь, это поможет.
Если вы хотите получить индекс последнего пробела в строке слова, вы можете использовать это выражение Право(название, (функция charindex(' ',обратный(наименование),0)), чтобы вернуть последнее слово в строке. Это полезно, если вы хотите разобрать фамилию полного имени, которое включает инициалы для первого и /или среднего имени.
@indexOf = <whatever characters you are searching for in your string>
@LastIndexOf = LEN([MyField]) - CHARINDEX(@indexOf, REVERSE([MyField]))не проверял, он может быть выключен на один из-за нулевого индекса, но работает в
SUBSTRINGфункция при отключении от@indexOfсимволы до конца строки
SUBSTRING([MyField], 0, @LastIndexOf)
мне нужно было найти 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','1234567893456789\', 1) as indexOf -- expect 0 (no instances) SELECT dbo.GetLastIndexOf('\','1234567893456789\', 1) as indexOf -- expect 20 SELECT dbo.GetLastIndexOf('\','1234567893456789\', 2) as indexOf -- expect 10 SELECT dbo.GetLastIndexOf('\','1234893456789\', 3) as indexOf -- expect 5
чтобы получить часть до последнего появления разделителя (работает только для
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)));
некоторые из других ответов возвращают фактическую строку, тогда как мне больше нужно было знать фактический индекс 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 (последний индекс':')
надеюсь, это сделало его немного легче кто-нибудь!
этот ответ соответствует требованиям OP. в частности, он позволяет игле быть больше, чем один символ, и он не генерирует ошибку, когда игла не найдена в стоге сена. Мне показалось, что больше всего (всего?) из других ответов не обрабатывались эти крайние случаи. Кроме того, я добавил аргумент "Начальная позиция", предоставленный собственной функцией MS SQL server CharIndex. Я попытался точно отразить спецификацию для 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
я наткнулся на этот поток при поиске решения моей аналогичной проблемы, которая имела точно такое же требование, но была для другого типа базы данных, которая также не имела
этот код работает, даже если подстрока содержит более 1 персонажа.
DECLARE @FilePath VARCHAR(100) = 'My_sub_Super_sub_Long_sub_String_sub_With_sub_Long_sub_Words' DECLARE @FindChar VARCHAR(5) = '_sub_' -- Shows text before last slash SELECT LEFT(@FilePath, LEN(@FilePath) - CHARINDEX(REVERSE(@FindChar), REVERSE(@FilePath)) - LEN(@FindChar) + 1) AS Before -- Shows text after last slash SELECT RIGHT(@FilePath, CHARINDEX(REVERSE(@FindChar), REVERSE(@FilePath)) -1) AS After -- Shows the position of the last slash SELECT LEN(@FilePath) - CHARINDEX(REVERSE(@FindChar), REVERSE(@FilePath)) AS LastOccuredAt
Comments