Existe-t-il un moyen simple de rechercher l'index de la dernière occurrence d'une chaîne à l'aide de SQL? J'utilise SQL Server 2000 en ce moment. J'ai essentiellement besoin de la fonctionnalité fournie par la méthode .NET System.String.LastIndexOf
. Une petite recherche sur Google a révélé ceci - Fonction Pour récupérer le dernier index - mais cela ne fonctionne pas si vous passez une expression de colonne "texte". Les autres solutions trouvées ailleurs ne fonctionnent que si le texte que vous recherchez contient un caractère.
Je vais probablement devoir cuisiner une fonction. Si je le fais, je le posterai ici pour que vous puissiez le consulter et peut-être vous en servir.
Vous êtes limité à petite liste de fonctions pour le type de données texte.
Tout ce que je peux suggérer, c'est de commencer par PATINDEX
, mais de revenir en arrière à partir de DATALENGTH-1, DATALENGTH-2, DATALENGTH-3
etc. jusqu'à obtenir un résultat ou d'arriver à zéro (DATALENTH-DATALENGTH)
C'est vraiment quelque chose que SQL Server 2000
ne peut tout simplement pas gérer.
Modifier pour d'autres réponses: REVERSE ne figure pas dans la liste des fonctions pouvant être utilisées avec des données texte dans SQL Server 2000
Manière simple? Non, mais j'ai utilisé l'inverse. Littéralement.
Dans les routines précédentes, pour trouver la dernière occurrence d'une chaîne donnée, j'avais utilisé la fonction REVERSE (), suivie de CHARINDEX, puis de nouveau par REVERSE pour rétablir l'ordre d'origine. Par exemple:
SELECT
mf.name
,mf.physical_name
,reverse(left(reverse(physical_name), charindex('\', reverse(physical_name)) -1))
from sys.master_files mf
montre comment extraire les noms de fichiers de la base de données à partir de leurs "noms physiques", quel que soit le degré d'imbrication dans les sous-dossiers. Ceci ne recherche qu'un seul caractère (la barre oblique inversée), mais vous pouvez en tirer parti pour des chaînes de recherche plus longues.
Le seul inconvénient est que je ne sais pas si cela fonctionnera bien avec les types de données TEXT. Je suis sur SQL 2005 depuis quelques années maintenant et je ne suis plus au courant de l’utilisation de TEXT - mais il me semble me souvenir que vous pourriez utiliser les fonctions GAUCHE et DROITE?
Philippe
Le moyen le plus simple est ....
REVERSE(SUBSTRING(REVERSE([field]),0,CHARINDEX('[expr]',REVERSE([field]))))
Si vous utilisez Sqlserver 2005 ou une version ultérieure, l’utilisation de la fonction REVERSE
à plusieurs reprises est préjudiciable aux performances. Le code ci-dessous est plus efficace.
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
Cela a très bien fonctionné pour moi.
REVERSE(SUBSTRING(REVERSE([field]), CHARINDEX(REVERSE('[expr]'), REVERSE([field])) + DATALENGTH('[expr]'), DATALENGTH([field])))
REVERSE(SUBSTRING(REVERSE(ap_description),CHARINDEX('.',REVERSE(ap_description)),len(ap_description)))
a mieux fonctionné pour moi
Ancienne question, mais toujours valable, voici donc ce que j’ai créé sur la base des informations fournies par d’autres ici.
create function fnLastIndexOf(@text varChar(max),@char varchar(1))
returns int
as
begin
return len(@text) - charindex(@char, reverse(@text)) -1
end
Hmm, je sais que c'est un ancien thread, mais une table de décompte pourrait le faire dans SQL2000 (ou toute autre base de données):
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
Une table de pointage est juste un tableau de nombres incrémentiels.
substring(@str, _Tally.n, 1) = @delim
obtient la position de chaque délimiteur, puis vous obtenez juste la position maximale dans cet ensemble.
Les tables de pointage sont géniales. Si vous ne les avez pas encore utilisées, il existe un bon article sur SQL Server Central (Free reg ou utilisez simplement Bug Me Not ( http://www.bugmenot.com/view/sqlservercentral.com )).
* EDIT: Suppression de n <= LEN(TEXT_FIELD)
, car vous ne pouvez pas utiliser LEN () sur le type TEXT. Tant que le substring(...) = @delim
reste, le résultat est toujours correct.
Inversez votre chaîne et votre sous-chaîne, puis recherchez la première occurrence.
Je réalise que cette question remonte à plusieurs années, mais ...
Sur Access 2010
, vous pouvez utiliser InStrRev()
pour le faire. J'espère que cela t'aides.
Certaines des autres réponses retournent une chaîne réelle alors que j'avais plus besoin de connaître l'index réel int. Et les réponses qui en découlent semblent compliquer les choses de manière excessive. En utilisant certaines des autres réponses comme source d’inspiration, j’ai fait ce qui suit ...
Tout d'abord, j'ai créé une fonction:
CREATE FUNCTION [dbo].[LastIndexOf] (@stringToFind varchar(max), @stringToSearch varchar(max))
RETURNS INT
AS
BEGIN
RETURN (LEN(@stringToSearch) - CHARINDEX(@stringToFind,REVERSE(@stringToSearch))) + 1
END
GO
Ensuite, dans votre requête, vous pouvez simplement faire ceci:
declare @stringToSearch varchar(max) = 'SomeText: SomeMoreText: SomeLastText'
select dbo.LastIndexOf(':', @stringToSearch)
Ce qui précède doit renvoyer 23 (le dernier index de ':')
J'espère que cela a facilité les choses pour quelqu'un!
Cette réponse utilise MS SQL Server 2008 (je n'ai pas accès à MS SQL Server 2000), mais la façon dont je le vois selon le PO comporte 3 situations à prendre en compte. De ce que je n'ai essayé aucune réponse ici couvre tous les 3 d'entre eux:
0
La fonction que j'ai créée prend 2 paramètres:
@String NVARCHAR(MAX)
: la chaîne à rechercher
@FindString NVARCHAR(MAX)
: un seul caractère ou une sous-chaîne pour obtenir le dernier index de in @String
Il retourne une INT
qui correspond à l'index positif de @FindString
dans @String
ou 0
, ce qui signifie que @FindString
n'est pas dans @String
.
Voici une explication de ce que fait la fonction:
@ReturnVal
à 0
en indiquant que @FindString
n'est pas dans @String
@FindString
dans @String
en utilisant CHARINDEX()
@FindString
dans @String
est 0
, @ReturnVal
est laissé sous la forme 0
@FindString
dans @String
est > 0
, @FindString
est dans @String
et donc Il calcule le dernier index de @FindString
dans @String
en utilisant REVERSE()
@ReturnVal
, qui est un nombre positif correspondant au dernier index de @FindString
dans @String
ou 0
, indiquant que @FindString
n'est pas dans @String
.Voici le script de création de fonction (copier-coller prêt):
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
Voici un peu qui teste commodément la fonction:
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)
Je ne l'ai exécuté que sur MS SQL Server 2008, car je n'ai accès à aucune autre version, mais d'après ce que j'ai examiné, cela devrait au moins être valable pour 2008+.
Prendre plaisir.
Je sais que cela sera inefficace, mais avez-vous envisagé de transformer le champ text
en varchar
afin de pouvoir utiliser la solution fournie par le site Web que vous avez trouvé? Je sais que cette solution créerait des problèmes car vous pourriez potentiellement tronquer l'enregistrement si la longueur du champ text
débordait de la longueur de votre varchar
(pour ne pas dire qu'il ne serait pas très performant).
Étant donné que vos données se trouvent dans un champ text
(et que vous utilisez SQL Server 2000), vos options sont limitées.
@indexOf = <whatever characters you are searching for in your string>
@LastIndexOf = LEN([MyField]) - CHARINDEX(@indexOf, REVERSE([MyField]))
N'a pas été testé, il pourrait être décalé de un à cause de l'index nul, mais fonctionne dans la fonction SUBSTRING
lorsqu'il coupe de @indexOf
caractères à la fin de votre chaîne.
SUBSTRING([MyField], 0, @LastIndexOf)
Cette réponse répond aux exigences du PO. spécifiquement, cela permet à l’aiguille d’être plus d’un caractère unique et ne génère pas d’erreur lorsque l’aiguille n’est pas trouvée dans une botte de foin. Il m'a semblé que la plupart (toutes?) Des autres réponses ne traitaient pas de ces cas Edge. Au-delà, j’ai ajouté l’argument "Position de départ" fourni par la fonction CharIndex native du serveur MS SQL. J'ai essayé de refléter exactement la spécification de CharIndex, sauf de traiter de droite à gauche au lieu de gauche à droite. Par exemple, je retourne null si aiguille ou meule de foin est null et je retourne zéro si aiguille n'est pas trouvée dans meule de foin. Une chose que je n'ai pas pu contourner, c'est qu'avec la fonction intégrée, le troisième paramètre est facultatif. Avec les fonctions définies par l'utilisateur SQL Server, tous les paramètres doivent être fournis dans l'appel, sauf si la fonction est appelée à l'aide de "EXEC". Bien que le troisième paramètre doive être inclus dans la liste des paramètres, vous pouvez indiquer le mot clé "default" sans avoir à lui attribuer de valeur (voir les exemples ci-dessous). Comme il est plus facile de supprimer le troisième paramètre de cette fonction si vous ne le souhaitez pas, il serait préférable de l'ajouter si nécessaire. Je l'ai inclus ici comme point de départ.
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
Pour obtenir la pièce avant la dernière occurrence du séparateur (fonctionne uniquement pour NVARCHAR
en raison de l'utilisation de DATALENGTH
):
DECLARE @Fullstring NVARCHAR(30) = '12.345.67890.ABC';
DECLARE @Delimiter CHAR(1) = '.';
SELECT SUBSTRING(@Fullstring, 1, DATALENGTH(@Fullstring)/2 - CHARINDEX(@Delimiter, REVERSE(@Fullstring)));
Ce code fonctionne même si la sous-chaîne contient plus d'un caractère.
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
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 également 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.
-- 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 l'évidence, 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
Je devais trouver la nième dernière position d'une barre oblique inverse dans un chemin de dossier ..__ Voici ma solution.
/*
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
Voici mes cas de test qui passent
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
Si vous voulez obtenir l'index du dernier espace dans une chaîne de mots, vous pouvez utiliser cette expression RIGHT (name, (CHARINDEX ('', REVERSE (name), 0)) pour renvoyer le dernier mot de la Ceci est utile si vous souhaitez analyser le nom de famille d'un nom complet comprenant les initiales du prénom et/ou du deuxième prénom.