Подтвердить что ты не робот

Есть ли LastIndexOf в SQL Server?

Я пытаюсь разобрать значение из строки, которая включает в себя получение last index string. В настоящее время я делаю ужасный взлом, который включает в себя реверсирование строки:

SELECT REVERSE(SUBSTRING(REVERSE(DB_NAME()), 1, 
    CHARINDEX('_', REVERSE(DB_NAME()), 1) - 1))

Мне этот код почти нечитабельен. Я просто обновился до SQL Server 2016, и я надеюсь, что есть лучший способ. Есть?

4b9b3361

Ответ 1

Если вы хотите все после последнего _, используйте:

select right(db_name(), charindex('_', reverse(db_name()) + '_') - 1)

Если вы хотите все раньше, используйте left():

select left(db_name(), len(db_name()) - charindex('_', reverse(db_name()) + '_'))

Ответ 2

Нет, SQL-сервер не имеет LastIndexOf.

Это доступная строка функции

Но вы всегда можете создать свою собственную функцию

CREATE FUNCTION dbo.LastIndexOf(@source text, @pattern char)  
RETURNS 
AS       
BEGIN  
    DECLARE @ret text;  
    SELECT into @ret
           REVERSE(SUBSTRING(REVERSE(@source), 1, 
           CHARINDEX(@pattern, REVERSE(@source), 1) - 1))
    RETURN @ret;  
END;  
GO 

Ответ 3

После того, как у вас есть одна из разделяющих строк здесь, вы можете сделать это на основе набора так, как это.

declare @string varchar(max)
set @string='C:\Program Files\Microsoft SQL Server\MSSQL\DATA\AdventureWorks_Data.mdf'

;with cte
as
(select *,row_number() over (order by (select null)) as rownum
from [dbo].[SplitStrings_Numbers](@string,'\')
)
select top 1 item from cte order by rownum desc

**Output:**  
AdventureWorks_Data.mdf

Ответ 4

Я столкнулся с этим потоком, ища решение моей аналогичной проблемы, которое имело то же самое требование, но было для другого типа базы данных, в которой отсутствовала функция REVERSE.

В моем случае это было для базы данных OpenEdge (Progress), которая имеет несколько иной синтаксис. Это сделало функцию INSTR доступной для меня тем, что большинство баз данных, основанных на Oracle, предлагают.

Итак, я придумал следующий код:

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

Однако для моей конкретной ситуации (являющейся базой данных OpenEdge (Progress)) это не приводило к желаемому поведению, потому что замена символа пустым char давала ту же длину, что и исходная строка. Это не имеет большого значения для меня, но я смог обойти проблему с помощью кода ниже:

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

Теперь я понимаю, что этот код не решит проблему для T-SQL, потому что нет альтернативы функции INSTR, которая предлагает свойство Occurence.

Чтобы быть осторожным, я добавлю код, необходимый для создания этой скалярной функции, поэтому его можно использовать так же, как в приведенных выше примерах. И будет делать то, что хотел OP, служит в качестве метода LastIndexOf для SQL Server.

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

Ответ 5

Написал 2 функции, 1 для возврата LastIndexOf для выбранного символа.

CREATE FUNCTION dbo.LastIndexOf(@source nvarchar(20), @pattern char)
RETURNS int
BEGIN  
       RETURN (LEN(@source)) -  CHARINDEX(@pattern, REVERSE(@source)) 
END;  
GO

и 1, чтобы вернуть строку до этого LastIndexOf. Может быть, кому-то это будет полезно.

CREATE FUNCTION dbo.StringBeforeLastIndex(@source nvarchar(20), @pattern char)
RETURNS nvarchar(20)
BEGIN  
       DECLARE @lastIndex int
       SET @lastIndex = (LEN(@source)) -  CHARINDEX(@pattern, REVERSE(@source)) 

     RETURN SUBSTRING(@source, 0, @lastindex + 1) 
     -- +1 because index starts at 0, but length at 1, so to get up to 11th index, we need LENGTH 11+1=12
END;  
GO

Ответ 6

CREATE FUNCTION dbo.LastIndexOf(@text NTEXT, @delimiter NTEXT)  
RETURNS INT
AS       
BEGIN  
  IF (@text IS NULL) RETURN NULL;
  IF (@delimiter IS NULL) RETURN NULL;
  DECLARE @Text2 AS NVARCHAR(MAX) = @text;
  DECLARE @Delimiter2 AS NVARCHAR(MAX) = @delimiter;
  DECLARE @Index AS INT = CHARINDEX(REVERSE(@Delimiter2), REVERSE(@Text2));
  IF (@Index < 1) RETURN 0;
  DECLARE @ContentLength AS INT = (LEN('|' + @Text2 + '|') - 2);
  DECLARE @DelimiterLength AS INT = (LEN('|' + @Delimiter2 + '|') - 2);
  DECLARE @Result AS INT = (@ContentLength - @Index - @DelimiterLength + 2);
  RETURN @Result;
END
  • Позволяет использовать многосимвольные разделители типа "," (запятая).
  • Возвращает 0, если разделитель не найден.
  • Принимает NTEXT для удобства, так как NVARCHAR (MAX) s неявно передаются в NTEXT, но не наоборот.
  • Правильно обрабатывает разделители с ведущим или хвостовым пространством!