SELECT REPLACE('<strong>100</strong><b>.00 GB', '%^(^-?\d*\.{0,1}\d+$)%', '');
Je veux remplacer n'importe quel balisage entre deux parties du nombre par la regex ci-dessus, mais cela ne semble pas fonctionner. Je ne sais pas si c'est la syntaxe regex qui est fausse, car j'ai essayé une méthode plus simple, telle que '%[^0-9]%'
, juste pour tester, mais cela n'a pas fonctionné non plus. Est-ce que quelqu'un sait comment puis-je y parvenir?
Vous pouvez utiliser PATINDEX Pour trouver le premier index de l'occurrence du modèle (chaîne). Ensuite, utilisez STUFF pour insérer une autre chaîne dans le motif (chaîne) correspondant.
Boucle à travers chaque ligne. Remplacez chaque caractère illégal par ce que vous voulez. Dans votre cas, remplacez non numérique par un blanc. La boucle interne est si vous avez plus d’un caractère illégal dans une cellule actuelle celui de la boucle.
DECLARE @counter int
SET @counter = 0
WHILE(@counter < (SELECT MAX(ID_COLUMN) FROM Table))
BEGIN
WHILE 1 = 1
BEGIN
DECLARE @RetVal varchar(50)
SET @RetVal = (SELECT Column = STUFF(Column, PATINDEX('%[^0-9.]%', Column),1, '')
FROM Table
WHERE ID_COLUMN = @counter)
IF(@RetVal IS NOT NULL)
UPDATE Table SET
Column = @RetVal
WHERE ID_COLUMN = @counter
ELSE
break
END
SET @counter = @counter + 1
END
Attention: c'est lent quand même! Avoir une colonne varchar peut avoir un impact. Donc, utiliser LTRIM RTRIM peut aider un peu. Peu importe, c'est lent.
Le crédit va à this StackOverFlow answer.
EDITCredit va également à @srutzky
Éditer (par @Tmdean) Au lieu d’effectuer une ligne à la fois, cette réponse peut être adaptée à une solution davantage basée sur les ensembles. Il itère toujours le maximum du nombre de caractères non numériques dans une seule ligne, ce n'est donc pas idéal, mais je pense que cela devrait être acceptable dans la plupart des situations.
WHILE 1 = 1 BEGIN
WITH q AS
(SELECT ID_Column, PATINDEX('%[^0-9.]%', Column) AS n
FROM Table)
UPDATE Table
SET Column = STUFF(Column, q.n, 1, '')
FROM q
WHERE Table.ID_Column = q.ID_Column AND q.n != 0;
IF @@ROWCOUNT = 0 BREAK;
END;
Vous pouvez également améliorer beaucoup l'efficacité si vous conservez une colonne de bits dans le tableau indiquant si le champ a déjà été nettoyé. (NULL représente "Inconnu" dans mon exemple et devrait être la colonne par défaut.)
DECLARE @done bit = 0;
WHILE @done = 0 BEGIN
WITH q AS
(SELECT ID_Column, PATINDEX('%[^0-9.]%', Column) AS n
FROM Table
WHERE COALESCE(Scrubbed_Column, 0) = 0)
UPDATE Table
SET Column = STUFF(Column, q.n, 1, ''),
Scrubbed_Column = 0
FROM q
WHERE Table.ID_Column = q.ID_Column AND q.n != 0;
IF @@ROWCOUNT = 0 SET @done = 1;
-- if Scrubbed_Column is still NULL, then the PATINDEX
-- must have given 0
UPDATE table
SET Scrubbed_Column = CASE
WHEN Scrubbed_Column IS NULL THEN 1
ELSE NULLIF(Scrubbed_Column, 0)
END;
END;
Si vous ne souhaitez pas modifier votre schéma, il est facile de l'adapter pour stocker les résultats intermédiaires dans une variable de valeur de table qui est appliquée à la table réelle à la fin.
De manière générale, SQL Server ne prend pas en charge les expressions régulières et vous ne pouvez pas les utiliser dans le code T-SQL natif.
Vous pourriez écrire une fonction CLR pour le faire. Voir ici , par exemple.
Au lieu de supprimer le personnage trouvé par sa seule position, l'utilisation de Replace(Column, BadFoundCharacter, '')
pourrait être considérablement plus rapide. En outre, au lieu de simplement remplacer le caractère incorrect suivant dans chaque colonne, il remplace tous ceux trouvés.
WHILE 1 = 1 BEGIN
UPDATE dbo.YourTable
SET Column = Replace(Column, Substring(Column, PatIndex('%[^0-9.-]%', Column), 1), '')
WHERE Column LIKE '%[^0-9.-]%'
If @@RowCount = 0 BREAK;
END;
Je suis convaincu que cela fonctionnera mieux que la réponse acceptée, ne serait-ce que parce que cela fait moins d'opérations. Il y a d'autres moyens qui pourraient aussi être plus rapides, mais je n'ai pas le temps de les explorer maintenant.
Je suis tombé par hasard sur ce post à la recherche de quelque chose d'autre, mais je pensais mentionner une solution que j'utilise qui est beaucoup plus efficace - et qui devrait être l'implémentation par défaut de toute fonction lorsqu'elle est utilisée avec une requête basée sur un ensemble - qui consiste à appliquer une croix fonction de table. On dirait que le sujet est toujours actif alors j'espère que cela sera utile à quelqu'un.
Exemple d'exécution sur quelques réponses jusqu'à présent, basé sur l'exécution de requêtes ou de fonctions scalaires basées sur des ensembles récursifs, basé sur un ensemble de tests de 1 m lignes supprimant les caractères d'un nouvel identifiant aléatoire forever} pour les exemples de fonctions.
L'utilisation d'une fonction de tableau avec application croisée permet d'atteindre le même objectif en 10s . Vous devrez peut-être l’ajuster en fonction de vos besoins, comme la longueur maximale qu’il gère.
Une fonction:
CREATE FUNCTION [dbo].[RemoveChars](@InputUnit VARCHAR(40))
RETURNS TABLE
AS
RETURN
(
WITH Numbers_prep(Number) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
)
,Numbers(Number) AS
(
SELECT TOP (ISNULL(LEN(@InputUnit),0))
row_number() OVER (ORDER BY (SELECT NULL))
FROM Numbers_prep a
CROSS JOIN Numbers_prep b
)
SELECT
OutputUnit
FROM
(
SELECT
substring(@InputUnit,Number,1)
FROM Numbers
WHERE substring(@InputUnit,Number,1) like '%[0-9]%'
ORDER BY Number
FOR XML PATH('')
) Sub(OutputUnit)
)
Usage:
UPDATE t
SET column = o.OutputUnit
FROM ##t t
CROSS APPLY [dbo].[RemoveChars](t.column) o
Voici une fonction récursive que j'ai écrite pour accomplir cela à partir des réponses précédentes.
CREATE FUNCTION dbo.RecursiveReplace
(
@P_String VARCHAR(MAX),
@P_Pattern VARCHAR(MAX),
@P_ReplaceString VARCHAR(MAX),
@P_ReplaceLength INT = 1
)
RETURNS VARCHAR(MAX)
BEGIN
DECLARE @Index INT;
-- Get starting point of pattern
SET @Index = PATINDEX(@P_Pattern, @P_String);
IF @Index > 0
BEGIN
-- Perform the replace
SET @P_String = STUFF(@P_String, PATINDEX(@P_Pattern, @P_String), @P_ReplaceLength, @P_ReplaceString);
-- Recurse
SET @P_String = dbo.RecursiveReplace(@P_String, @P_Pattern, @P_ReplaceString, @P_ReplaceLength);
END;
RETURN @P_String;
END;
Intégrer la solution dans une fonction SQL pourrait être utile si vous souhaitez la réutiliser . Je le fais même au niveau de la cellule, c'est pourquoi je propose une réponse différente:
CREATE FUNCTION [dbo].[fnReplaceInvalidChars] (@string VARCHAR(300))
RETURNS VARCHAR(300)
BEGIN
DECLARE @str VARCHAR(300) = @string;
DECLARE @Pattern VARCHAR (20) = '%[^a-zA-Z0-9]%';
DECLARE @Len INT;
SELECT @Len = LEN(@String);
WHILE @Len > 0
BEGIN
SET @Len = @Len - 1;
IF (PATINDEX(@Pattern,@str) > 0)
BEGIN
SELECT @str = STUFF(@str, PATINDEX(@Pattern,@str),1,'');
END
ELSE
BEGIN
BREAK;
END
END
RETURN @str
END
Si vous faites cela uniquement pour un paramètre entrant dans une procédure stockée, vous pouvez utiliser les éléments suivants:
while PatIndex('%[^0-9]%', @Param) > 0
select @Param = Replace(@Param, Substring(@Param, PatIndex('%[^0-9]%', @Param), 1), '')
Je pense qu'une approche plus simple et plus rapide consiste à parcourir chaque caractère de l'alphabet:
DECLARE @i int
SET @i = 0
WHILE(@i < 256)
BEGIN
IF char(@i) NOT IN ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.')
UPDATE Table SET Column = replace(Column, char(@i), '')
SET @i = @i + 1
END
J'ai créé cette fonction pour nettoyer une chaîne contenant des caractères non numériques dans un champ time. L'heure contenait des points d'interrogation quand ils n'ont pas ajouté les procès-verbaux, quelque chose comme ceci: 20: ??. La fonction parcourt chaque caractère et remplace le? avec un 0:
CREATE FUNCTION [dbo].[CleanTime]
(
-- Add the parameters for the function here
@intime nvarchar(10)
)
RETURNS nvarchar(5)
AS
BEGIN
-- Declare the return variable here
DECLARE @ResultVar nvarchar(5)
DECLARE @char char(1)
-- Add the T-SQL statements to compute the return value here
DECLARE @i int = 1
WHILE @i <= LEN(@intime)
BEGIN
SELECT @char = CASE WHEN substring(@intime,@i,1) like '%[0-9:]%' THEN substring(@intime,@i,1) ELSE '0' END
SELECT @ResultVar = concat(@ResultVar,@char)
set @i = @i + 1
END;
-- Return the result of the function
RETURN @ResultVar
END