web-dev-qa-db-fra.com

Comment remplacer plusieurs caractères dans SQL?

Ceci est basé sur une question similaire Comment remplacer plusieurs caractères dans Access SQL?

J'ai écrit ceci car SQL Server 2005 semble avoir une limite sur la fonction replace () à 19 remplacements dans une clause where.

J'ai la tâche suivante: Nécessité d'effectuer une correspondance sur une colonne et d'améliorer les chances qu'une correspondance supprime plusieurs caractères inutiles à l'aide de la fonction replace ()

DECLARE @es NVarChar(1) SET @es = ''
DECLARE @p0 NVarChar(1) SET @p0 = '!'
DECLARE @p1 NVarChar(1) SET @p1 = '@'
---etc...

SELECT *
FROM t1,t2 
WHERE  REPLACE(REPLACE(t1.stringkey,@p0, @es), @p1, @es) 
     = REPLACE(REPLACE(t2.stringkey,@p0, @es), @p1, @es)    
---etc 

S'il y a> 19 REPLACE () dans cette clause where, cela ne fonctionne pas. La solution que j’ai proposée est donc de créer une fonction SQL appelée trimChars dans cet exemple (excusez-les à partir de @ 22

CREATE FUNCTION [trimChars] (
   @string varchar(max)
) 

RETURNS varchar(max) 
AS
BEGIN

DECLARE @es NVarChar(1) SET @es = ''
DECLARE @p22 NVarChar(1) SET @p22 = '^'
DECLARE @p23 NVarChar(1) SET @p23 = '&'
DECLARE @p24 NVarChar(1) SET @p24 = '*'
DECLARE @p25 NVarChar(1) SET @p25 = '('
DECLARE @p26 NVarChar(1) SET @p26 = '_'
DECLARE @p27 NVarChar(1) SET @p27 = ')'
DECLARE @p28 NVarChar(1) SET @p28 = '`'
DECLARE @p29 NVarChar(1) SET @p29 = '~'
DECLARE @p30 NVarChar(1) SET @p30 = '{'

DECLARE @p31 NVarChar(1) SET @p31 = '}'
DECLARE @p32 NVarChar(1) SET @p32 = ' '
DECLARE @p33 NVarChar(1) SET @p33 = '['
DECLARE @p34 NVarChar(1) SET @p34 = '?'
DECLARE @p35 NVarChar(1) SET @p35 = ']'
DECLARE @p36 NVarChar(1) SET @p36 = '\'
DECLARE @p37 NVarChar(1) SET @p37 = '|'
DECLARE @p38 NVarChar(1) SET @p38 = '<'
DECLARE @p39 NVarChar(1) SET @p39 = '>'
DECLARE @p40 NVarChar(1) SET @p40 = '@'
DECLARE @p41 NVarChar(1) SET @p41 = '-'

return   REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
       @string, @p22, @es), @p23, @es), @p24, @es), @p25, @es), @p26, @es), @p27, @es), @p28, @es), @p29, @es), @p30, @es), @p31, @es), @p32, @es), @p33, @es), @p34, @es), @p35, @es), @p36, @es), @p37, @es), @p38, @es), @p39, @es), @p40, @es), @p41, @es)
END 

Ceci peut alors être utilisé en plus des autres chaînes de remplacement

SELECT *
FROM t1,t2 
WHERE  trimChars(REPLACE(REPLACE(t1.stringkey,@p0, @es), @p1, @es) 
         = REPLACE(REPLACE(t2.stringkey,@p0, @es), @p1, @es))   

J'ai créé quelques fonctions supplémentaires pour effectuer un remplacement similaire, comme so trimChars (trimMoreChars (

SELECT *
FROM t1,t2 
WHERE  trimChars(trimMoreChars(REPLACE(REPLACE(t1.stringkey,@p0, @es), @p1, @es) 
         = REPLACE(REPLACE(t2.stringkey,@p0, @es), @p1, @es)))

Quelqu'un peut-il me donner une meilleure solution à ce problème en termes de performances et peut-être une mise en œuvre plus propre?

33
kiev

Je considérerais sérieusement faisant un UDF CLR } _ à la place et utilisant des expressions régulières (la chaîne et le motif peuvent être passés en paramètres) personnages. Il devrait facilement surpasser cet UDF SQL.

22
Cade Roux

Une astuce utile en SQL est la possibilité d’utiliser @var = function(...) pour attribuer une valeur. Si vous avez plusieurs enregistrements dans votre jeu d'enregistrements, votre var est affecté plusieurs fois avec des effets secondaires:

declare @badStrings table (item varchar(50))

INSERT INTO @badStrings(item)
SELECT '>' UNION ALL
SELECT '<' UNION ALL
SELECT '(' UNION ALL
SELECT ')' UNION ALL
SELECT '!' UNION ALL
SELECT '?' UNION ALL
SELECT '@'

declare @testString varchar(100), @newString varchar(100)

set @teststring = 'Juliet ro><0zs my s0x()rz!!?!one!@!@!@!'
set @newString = @testString

SELECT @newString = Replace(@newString, item, '') FROM @badStrings

select @newString -- returns 'Juliet ro0zs my s0xrzone'
46
Juliet

J'aime beaucoup la solution de @ Juliett! Je voudrais juste utiliser un CTE pour obtenir tous les caractères non valides:

DECLARE @badStrings VARCHAR(100)
DECLARE @teststring VARCHAR(100)

SET @badStrings = '><()!?@'
SET @teststring = 'Juliet ro><0zs my s0x()rz!!?!one!@!@!@!'

;WITH CTE AS
(
  SELECT SUBSTRING(@badStrings, 1, 1) AS [String], 1 AS [Start], 1 AS [Counter]
  UNION ALL
  SELECT SUBSTRING(@badStrings, [Start] + 1, 1) AS [String], [Start] + 1, [Counter] + 1 
  FROM CTE 
  WHERE [Counter] < LEN(@badStrings)
)

SELECT @teststring = REPLACE(@teststring, CTE.[String], '') FROM CTE

SELECT @teststring

Juliet ro0zs mon s0xrzone

16
Duanne

Je vous suggère de créer une fonction définie par l'utilisateur scalaire. Ceci est un exemple (désolé d'avance, car les noms des variables sont en espagnol):

CREATE FUNCTION [dbo].[Udf_ReplaceChars] (
  @cadena VARCHAR(500),  -- String to manipulate
  @caracteresElim VARCHAR(100),  -- String of characters to be replaced
  @caracteresReem VARCHAR(100)   -- String of characters for replacement
) 
RETURNS VARCHAR(500)
AS
BEGIN
  DECLARE @cadenaFinal VARCHAR(500), @longCad INT, @pos INT, @caracter CHAR(1), @posCarER INT;
  SELECT
    @cadenaFinal = '',
    @longCad = LEN(@cadena),
    @pos = 1;

  IF LEN(@caracteresElim)<>LEN(@caracteresReem)
    BEGIN
      RETURN NULL;
    END

  WHILE @pos <= @longCad
    BEGIN
      SELECT
        @caracter = SUBSTRING(@cadena,@pos,1),
        @pos = @pos + 1,
        @posCarER = CHARINDEX(@caracter,@caracteresElim);

      IF @posCarER <= 0
        BEGIN
          SET @cadenaFinal = @cadenaFinal + @caracter;
        END
      ELSE
        BEGIN
          SET @cadenaFinal = @cadenaFinal + SUBSTRING(@caracteresReem,@posCarER,1)
        END
    END

  RETURN @cadenaFinal;
END

Voici un exemple utilisant cette fonction:

SELECT dbo.Udf_ReplaceChars('This is a test.','sat','Z47');

Et le résultat est: 7hiZ iZ 4 7eZ7.

Comme vous pouvez le constater, chaque caractère du paramètre @caracteresElim est remplacé par le caractère situé au même emplacement à partir du paramètre @caracteresReem.

3
declare @testVal varchar(20)

set @testVal = '?t/es?ti/n*g 1*2?3*'

select @testVal = REPLACE(@testVal, item, '') from (select '?' item union select '*' union select '/') list

select @testVal;
1
Adil

Une option consiste à utiliser une table de nombres/comptage pour diriger un processus itératif via une requête basée sur un pseudo-ensemble.

L'idée générale de remplacement de caractères peut être démontrée avec une approche de table de carte de caractères simple:

create table charMap (srcChar char(1), replaceChar char(1))
insert charMap values ('a', 'z')
insert charMap values ('b', 'y')


create table testChar(srcChar char(1))
insert testChar values ('1')
insert testChar values ('a')
insert testChar values ('2')
insert testChar values ('b')

select 
coalesce(charMap.replaceChar, testChar.srcChar) as charData
from testChar left join charMap on testChar.srcChar = charMap.srcChar

Ensuite, vous pouvez utiliser l'approche de la table de pointage pour effectuer la recherche sur chaque position du caractère dans la chaîne.

create table tally (i int)
declare @i int
set @i = 1
while @i <= 256 begin
    insert tally values (@i)
    set @i = @i + 1
end

create table testData (testString char(10))
insert testData values ('123a456')
insert testData values ('123ab456')
insert testData values ('123b456')

select
    i,
    SUBSTRING(testString, i, 1) as srcChar,
    coalesce(charMap.replaceChar, SUBSTRING(testString, i, 1)) as charData
from testData cross join tally
    left join charMap on SUBSTRING(testString, i, 1) = charMap.srcChar
where i <= LEN(testString)
0
ahains

Bien que cette question concerne SQL Server 2005, il convient de noter que depuis SQL Server 2017, la requête peut être effectuée à l'aide de la nouvelle fonction TRANSLATE.

https://docs.Microsoft.com/en-us/sql/t-sql/functions/translate-transact-sql

J'espère que cette information aidera les personnes qui accèdent à cette page à l'avenir.

0
Ethan1701

Je ne sais pas pourquoi Charles Bretana a supprimé sa réponse. Je l'ajoute donc sous forme de réponse CW, mais une colonne calculée persistante est un VRAIMENT bon moyen de gérer ces cas où vous avez besoin presque toujours de données nettoyées ou transformées. , mais il faut préserver les ordures d'origine. Sa suggestion est pertinente et appropriée, peu importe la façon dont vous décidez de nettoyer vos données.

Plus précisément, dans mon projet actuel, j'ai une colonne calculée persistante qui élimine tous les zéros non significatifs (heureusement, cette tâche est facilement gérée de manière simple dans T-SQL) à partir de certains identificateurs numériques stockés de manière incohérente avec les zéros non significatifs. Ceci est stocké dans des colonnes calculées persistantes dans les tables qui en ont besoin et indexé car cet identifiant conforme est souvent utilisé dans les jointures.

0
Cade Roux

Voici les étapes 

  1. Créer une fonction CLR

Voir le code suivant:

public partial class UserDefinedFunctions 
{

[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString Replace2(SqlString inputtext, SqlString filter,SqlString      replacewith)
{

    string str = inputtext.ToString();
    try
    {
        string pattern = (string)filter;
        string replacement = (string)replacewith;
        Regex rgx = new Regex(pattern);
        string result = rgx.Replace(str, replacement);
        return (SqlString)result;

    }
    catch (Exception s)
    {
        return (SqlString)s.Message;
    }
}
}
  1. Déployez votre fonction CLR

  2. Maintenant le tester

Voir le code suivant:

create table dbo.test(dummydata varchar(255))
Go
INSERT INTO dbo.test values('P@ssw1rd'),('This 12is @test')
Go
Update dbo.test
set dummydata=dbo.Replace2(dummydata,'[0-9@]','')

select * from dbo.test
dummydata, Psswrd, This is test booom!!!!!!!!!!!!!
0
HimalayanNinja