web-dev-qa-db-fra.com

Existe-t-il un LastIndexOf dans SQL Server?

J'essaie d'analyser une valeur d'une chaîne qui implique d'obtenir le lastindex d'un string . Actuellement, je fais un piratage horrible qui consiste à inverser une chaîne:

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

Pour moi, ce code est presque illisible. Je viens de mettre à niveau vers SQL Server 2016 et j'espère qu'il existe un meilleur moyen . Y a-t-il?

31
AngryHacker

Si vous voulez tout après le dernier _, utilisez:

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

Si vous voulez tout avant, utilisez left():

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

A écrit 2 fonctions, 1 pour renvoyer LastIndexOf pour le caractère sélectionné.

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

et 1 pour renvoyer une chaîne avant ce LastIndexOf. Peut-être que ce sera utile à quelqu'un.

CREATE FUNCTION dbo.StringBeforeLastIndex(@source nvarchar(80), @pattern char)
RETURNS nvarchar(80)
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
10
user2771704
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
  • Autorise les délimiteurs multi-caractères comme "," (espace de virgule). 
  • Renvoie 0 si le délimiteur n'est pas trouvé.
  • Prend un NTEXT pour des raisons de confort car les NVARCHAR (MAX) sont convertis implicitement en NTEXT, mais pas l’inverse.
  • Gère correctement les délimiteurs avec un espace de début ou de fin!
2
Christoph

Une fois que vous avez l’une des scinder les chaînes d’ici , vous pouvez le faire comme ci-dessous. 

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

Non, le serveur SQL n'a pas LastIndexOf.

Voici la chaîne disponible fonctions

Mais vous pouvez toujours créer votre propre fonction

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 
2
Juan Carlos Oropeza

Je suis tombé sur ce fil en cherchant une solution à mon problème similaire, qui répondait exactement aux mêmes exigences, mais concernait un type de base de données différent pour lequel il manquait la fonction REVERSE.

Dans mon cas, il s’agissait d’une base de données OpenEdge (Progress), dont la syntaxe est légèrement différente. Ceci a rendu la fonction INSTR disponible que la plupart des bases de données typées Oracle offrent .

Alors je suis venu avec le code suivant:

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

Cependant, pour ma situation spécifique (étant la base de données OpenEdge (Progress)), cela ne s'est pas traduit par le comportement souhaité, car remplacer le caractère par un caractère vide donnait la même longueur que la chaîne d'origine. Cela n'a pas beaucoup de sens pour moi mais j'ai pu contourner le problème avec le code ci-dessous:

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

Je comprends maintenant que ce code ne résoudra pas le problème de T-SQL car il n’existe pas d’alternative à la fonction INSTR qui offre la propriété Occurence.

Par souci de précision, je vais ajouter le code nécessaire à la création de cette fonction scalaire pour pouvoir l’utiliser de la même manière que dans les exemples ci-dessus. Et fera exactement ce que l'OP voulait, sert de méthode LastIndexOf pour 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

Pour éviter ce qui est évident, lorsque la fonction REVERSE est disponible, vous n'avez pas besoin de créer cette fonction scalaire et vous pouvez simplement obtenir le résultat souhaité comme ceci:

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