web-dev-qa-db-fra.com

Comment supprimer les accents et tous les caractères <> a..z dans sql-server?

Je dois apporter les modifications suivantes à un champ varchar (20):

  1. remplacez les accents par des lettres normales (comme è à e)
  2. after (1) supprime tous les caractères non en a..z

par exemple

'aèàç=.32s df' 

doit devenir 

'aeacsdf'

existe-t-il des fonctions spéciales mémorisées pour y parvenir facilement?

UPDATE: veuillez fournir une solution T-SQL et non CLR. C’est la solution que j’ai temporairement adoptée parce que cela répond temporairement à mes besoins, mais il serait préférable d’utiliser une approche plus élégante.

CREATE FUNCTION sf_RemoveExtraChars (@NAME nvarchar(50))
RETURNS nvarchar(50)
AS
BEGIN
  declare @TempString nvarchar(100)
  set @TempString = @NAME 
  set @TempString = LOWER(@TempString)
  set @TempString =  replace(@TempString,' ', '')
  set @TempString =  replace(@TempString,'à', 'a')
  set @TempString =  replace(@TempString,'è', 'e')
  set @TempString =  replace(@TempString,'é', 'e')
  set @TempString =  replace(@TempString,'ì', 'i')
  set @TempString =  replace(@TempString,'ò', 'o')
  set @TempString =  replace(@TempString,'ù', 'u')
  set @TempString =  replace(@TempString,'ç', 'c')
  set @TempString =  replace(@TempString,'''', '')
  set @TempString =  replace(@TempString,'`', '')
  set @TempString =  replace(@TempString,'-', '')
  return @TempString
END
GO
37
LaBracca

Ce que vous cherchez, c’est quelque chose à enlever Diacritiques de personnages individuels. Je crains que votre solution ne soit presque aussi bonne que possible, du moins avec du SQL pur. dotNet/CLR fournit une méthode simple pour le faire, cependant. Désolé, je sais que vous souhaitez éviter une autre solution CLR, mais Microsoft SQL Server ne fournit pas d'équivalent T-SQL pour cela.

Si vous êtes chanceux, vous avez le collation défini dans votre base de données sous la forme "SQL_Latin1_General_CP1_CI_AS" ou toute variante commençant par "SQL_Latin1_General". Cela équivaut à Windows-1252 qui est très bien documenté . Vous pourrez "traduire" chaque caractère en un équivalent anglais en examinant les caractères et en mappant un équivalent à l'aide d'une instruction SQL CASE comme vous l'avez été.

J'ai une correction rapide pour votre code, cependant. Vous allez vouloir utiliser varchar dans vos variables et paramètres. Cela crée une surcharge supplémentaire en effectuant des conversions de types de données et peut introduire des caractères Unicode qui n'existent que sous la forme Unicode dans le mixage. De plus, vous trouverez une raison liée à la sécurité spécifique à votre situation sur le blog de Bruce Schneier .

Mise à jour Le blog de Michael S Kaplan contient quelques informations utiles sur l’internationalisation de Windows et de la diacritique et de Windows.

15
jveazey

Le meilleur moyen d'y parvenir est très simple et efficace:

SELECT 'àéêöhello!' Collate SQL_Latin1_General_CP1253_CI_AI

qui produit 'aeeohello!'

La chaîne ne doit pas être unicode. Si vous avez un nvarchar, lancez-le simplement dans varchar avant d'utiliser l'assemblage.

Voici une fonction qui répond aux besoins du PO:

create function [dbo].[RemoveExtraChars] ( @p_OriginalString varchar(50) )
returns varchar(50) as
begin

  declare @i int = 1;  -- must start from 1, as SubString is 1-based
  declare @OriginalString varchar(100) = @p_OriginalString Collate SQL_Latin1_General_CP1253_CI_AI;
  declare @ModifiedString varchar(100) = '';

  while @i <= Len(@OriginalString)
  begin
    if SubString(@OriginalString, @i, 1) like '[a-Z]'
    begin
      set @ModifiedString = @ModifiedString + SubString(@OriginalString, @i, 1);
    end
    set @i = @i + 1;
  end

  return @ModifiedString

end

Ensuite, la commande:

select dbo.RemoveExtraChars('aèàç=.32s df')

les sorties

aeacsdf
96
Dominic Goulet
    SELECT 'áéíóú' COLLATE Cyrillic_General_CI_AI

Cela remplacera tous les caractères accentués ...

résultat: aeiou

J'espère que cela vous aidera!

6
Eduardo Caltempa

Permettez-moi de préciser quelque chose en premier: les caractères accentués que vous montrez ne sont pas réellement Unicode (comme l’implique une réponse); ce sont des caractères ASCII de 8 bits. Une chose à garder à l’esprit: vous voyez des caractères comme è et à tout simplement parce que c’est ainsi que votre page de codes (la page de codes utilisée par votre système d’exploitation et/ou SQL Server [je ne sais pas lequel]] les affiche. Dans une page de code différente, ces caractères seraient représentés par des symboles totalement différents (par exemple, si vous utilisez une page de code cyrillique ou turque).

Quoi qu'il en soit, supposons que vous souhaitiez remplacer ces caractères de 8 bits par l'équivalent américain/latin le plus proche pour votre page de codes par défaut [je suppose que ce sont des caractères provenant d'une variante d'un jeu de caractères latins]. C’est ainsi que j’ai abordé un problème similaire (avertissement: ce n’est pas une solution très élégante, mais je ne pouvais penser à rien de mieux à l’époque):

Créez une fonction utilisateur pour traduire un caractère ASCII de 8 bits en un équivalent imprimable ASCII de 7 bits, tel que:

CREATE FUNCTION dbo.fnCharToAscii
(
  @Char AS VARCHAR
)
RETURNS
  VARCHAR   
AS
BEGIN
IF (@Char IS NULL)
  RETURN ''

-- Process control and DEL chars.
IF (ASCII(@Char) < 32) OR (ASCII(@Char) = 127)
    RETURN ''

-- Return printable 7-bit ASCII chars as is.
-- UPDATE TO DELETE NON-ALPHA CHARS.
IF (ASCII(@Char) >= 32) AND (ASCII(@Char) < 127)
    RETURN @Char

-- Process 8-bit ASCII chars.
RETURN
  CASE ASCII(@Char)
    WHEN 128 THEN 'E'
    WHEN 129 THEN '?'
    WHEN 130 THEN ','
    WHEN 131 THEN 'f'
    WHEN 132 THEN ','
    WHEN 133 THEN '.'
    WHEN 134 THEN '+'
    WHEN 135 THEN '+'
    WHEN 136 THEN '^'
    WHEN 137 THEN '%'
    WHEN 138 THEN 'S'
    WHEN 139 THEN '<'
    WHEN 140 THEN 'C'
    WHEN 141 THEN '?'
    WHEN 142 THEN 'Z'
    WHEN 143 THEN '?'
    WHEN 144 THEN '?'
    WHEN 145 THEN ''''
    WHEN 146 THEN ''''
    WHEN 147 THEN '"'
    WHEN 148 THEN '"'
    WHEN 149 THEN '-'
    WHEN 150 THEN '-'
    WHEN 151 THEN '-'
    WHEN 152 THEN '~'
    WHEN 153 THEN '?'
    WHEN 154 THEN 's'
    WHEN 155 THEN '>'
    WHEN 156 THEN 'o'
    WHEN 157 THEN '?'
    WHEN 158 THEN 'z'
    WHEN 159 THEN 'Y'
    WHEN 160 THEN ' '
    WHEN 161 THEN 'i'
    WHEN 162 THEN 'c'
    WHEN 163 THEN 'L'
    WHEN 164 THEN '?'
    WHEN 165 THEN 'Y'
    WHEN 166 THEN '|'
    WHEN 167 THEN '$'
    WHEN 168 THEN '^'
    WHEN 169 THEN 'c'
    WHEN 170 THEN 'a'
    WHEN 171 THEN '<'
    WHEN 172 THEN '-'
    WHEN 173 THEN '-'
    WHEN 174 THEN 'R'
    WHEN 175 THEN '-'
    WHEN 176 THEN 'o'
    WHEN 177 THEN '+'
    WHEN 178 THEN '2'
    WHEN 179 THEN '3'
    WHEN 180 THEN ''''
    WHEN 181 THEN 'm'
    WHEN 182 THEN 'P'
    WHEN 183 THEN '-'
    WHEN 184 THEN ','
    WHEN 185 THEN '1'
    WHEN 186 THEN '0'
    WHEN 187 THEN '>'
    WHEN 188 THEN '?'
    WHEN 189 THEN '?'
    WHEN 190 THEN '?'
    WHEN 191 THEN '?'
    WHEN 192 THEN 'A'
    WHEN 193 THEN 'A'
    WHEN 194 THEN 'A'
    WHEN 195 THEN 'A'
    WHEN 196 THEN 'A'
    WHEN 197 THEN 'A'
    WHEN 198 THEN 'A'
    WHEN 199 THEN 'C'
    WHEN 200 THEN 'E'
    WHEN 201 THEN 'E'
    WHEN 202 THEN 'E'
    WHEN 203 THEN 'E'
    WHEN 204 THEN 'I'
    WHEN 205 THEN 'I'
    WHEN 206 THEN 'I'
    WHEN 207 THEN 'I'
    WHEN 208 THEN 'D'
    WHEN 209 THEN 'N'
    WHEN 210 THEN 'O'
    WHEN 211 THEN 'O'
    WHEN 212 THEN 'O'
    WHEN 213 THEN 'O'
    WHEN 214 THEN 'O'
    WHEN 215 THEN 'x'
    WHEN 216 THEN 'O'
    WHEN 217 THEN 'U'
    WHEN 218 THEN 'U'
    WHEN 219 THEN 'U'
    WHEN 220 THEN 'U'
    WHEN 221 THEN 'Y'
    WHEN 222 THEN 'b'
    WHEN 223 THEN 'B'
    WHEN 224 THEN 'a'
    WHEN 225 THEN 'a'
    WHEN 226 THEN 'a'
    WHEN 227 THEN 'a'
    WHEN 228 THEN 'a'
    WHEN 229 THEN 'a'
    WHEN 230 THEN 'a'
    WHEN 231 THEN 'c'
    WHEN 232 THEN 'e'
    WHEN 233 THEN 'e'
    WHEN 234 THEN 'e'
    WHEN 235 THEN 'e'
    WHEN 236 THEN 'i'
    WHEN 237 THEN 'i'
    WHEN 238 THEN 'i'
    WHEN 239 THEN 'i'
    WHEN 240 THEN 'o'
    WHEN 241 THEN 'n'
    WHEN 242 THEN 'o'
    WHEN 243 THEN 'o'
    WHEN 244 THEN 'o'
    WHEN 245 THEN 'o'
    WHEN 246 THEN 'o'
    WHEN 247 THEN '-'
    WHEN 248 THEN 'o'
    WHEN 249 THEN 'u'
    WHEN 250 THEN 'u'
    WHEN 251 THEN 'u'
    WHEN 252 THEN 'u'
    WHEN 253 THEN 'y'
    WHEN 254 THEN 'b'
    WHEN 255 THEN 'y'
  END
RETURN ''
END

Le code ci-dessus est à usage général, vous pouvez donc ajuster les mappages de caractères pour supprimer tous les caractères non alphabétiques, par exemple. vous pouvez utiliser un code comme celui-ci dans la correspondance pour le caractère imprimable ASCII 7 bits (cela suppose un classement ne respectant pas la casse):

IF @Char NOT LIKE '[a-z]' RETURN ''

Pour voir si le mappage des caractères pour les symboles ASCII 8 bits fonctionne correctement, exécutez le code suivant:

DECLARE @I   INT
DECLARE @Msg VARCHAR(32)

SET @I = 128

WHILE @I < 256
BEGIN
    SELECT @Msg = CAST(@I AS VARCHAR) + 
    ': ' + 
    CHAR(@I) + 
    '=' + 
    dbo.fnCharToAscii(CHAR(@I))
    PRINT @Msg
    SET @I = @I + 1 
END

Vous pouvez maintenant créer un fichier UDF pour traiter une chaîne:

CREATE FUNCTION dbo.fnStringToAscii
(
  @Value AS VARCHAR(8000)
)
RETURNS
  VARCHAR(8000) 
AS
BEGIN
IF (@Value IS NULL OR DATALENGTH(@Value) = 0)
  RETURN ''

DECLARE @Index  INT
DECLARE @Result VARCHAR(8000)

SET @Result = ''
SET @Index  = 1

WHILE (@Index <= DATALENGTH(@Value))
BEGIN
  SET @Result = @Result + dbo.fnCharToAscii(SUBSTRING(@Value, @Index, 1))
  SET @Index = @Index + 1   
END

RETURN @Result
END
GO
5
Alek Davis

Vous pouvez éviter les instructions REPLACE codées de manière irréversible en utilisant une clause COLLATE avec un classement sans accent particulier pour comparer les caractères alphabétiques accentués à ceux non alphabétiques: 

DECLARE 
  @s1 NVARCHAR(200),
  @s2 NVARCHAR(200)

SET @s1 = N'aèàç=.32s df' 

SET @s2 = N''
SELECT @s2 = @s2 + no_accent 
FROM ( 
  SELECT 
    SUBSTRING(@s1, number, 1) AS accent,
    number
  FROM master.dbo.spt_values 
  WHERE TYPE = 'P'
  AND number BETWEEN 1 AND LEN(@s1) 
) s1 
INNER JOIN (
  SELECT NCHAR(number) AS no_accent
  FROM master.dbo.spt_values 
  WHERE type = 'P'
  AND (number BETWEEN 65 AND 90 OR number BETWEEN 97 AND 122) 
) s2 
  ON s1.accent COLLATE LATIN1_GENERAL_CS_AI = s2.no_accent 
ORDER BY number

SELECT @s1 
SELECT @s2 

/*
aèàç=.32s df
aeacsdf
*/
4
8kb

Eh bien, ce n'est pas beaucoup mieux, mais c'est au moins une solution d'ensemble tsql

declare @TempString varchar(100)

set @TempString='textàè containing éìòaccentsç''''` and things-'

select @TempString=
    replace(
        replace(
            replace(
                replace(
                    replace(
                        replace(
                            replace(
                                replace(
                                    replace(
                                        replace(
                                            replace(@TempString,' ', '') 
                                        ,'à', 'a')
                                    ,'è', 'e') 
                                ,'é', 'e')
                            ,'ì', 'i')
                        ,'ò', 'o') 
                    ,'ù', 'u') 
                ,'ç', 'c') 
            ,'''', '') 
        ,'`', '')
    ,'-', '') 



select @TempString
2
DForck42

Autant que je sache, il n’existe pas de mappage direct pour les caractères unicode/UTF-X qui "se ressemblent". À moins que quelqu'un ait quelque chose de beaucoup plus cool, je suggérerais d'adopter une approche de force brute afin que vous puissiez faire votre travail jusque-là. 

On dirait que vous devez faire 2 passes. La première étape consiste à remplacer les lettres qui se ressemblent en premier, puis à supprimer toutes les lettres non anglaises restantes.

Cet article peut vous aider à créer une fonction définie par l'utilisateur afin que vous puissiez utiliser des expressions régulières au lieu de dizaines d'appels REPLACE: http://msdn.Microsoft.com/en-us/magazine/cc163473.aspx

Voici un dictionnaire que j'ai utilisé pour ce cas:

    public static Dictionary<char, string> NonEnglishLetterMapping = new Dictionary<char, string>
    {
          {'a', "áàâãäåāăą"}
        //, {'b', ""}
        , {'c', "ćĉċč"}
        , {'d', "ďđ"}
        , {'e', "éëêèēĕėę"}
        //, {'f', ""}
        , {'g', "ĝğġģ"}
        , {'h', "ĥħ"}
        , {'i', "ìíîïĩīĭįı"}
        , {'j', "ĵ"}
        , {'k', "ķĸ"}
        , {'l', "ĺļľŀł"}
        //, {'m', ""}
        , {'n', "ñńņňʼnŋ"}
        , {'o', "òóôõöōŏőơ"}
        //, {'p', ""}
        //, {'q', ""}
        , {'r', "ŕŗř"}
        , {'s', "śŝşšș"}
        , {'t', "ţťŧț"}
        , {'u', "ùúûüũūŭůűųư"}
        //, {'v', ""}
        , {'w', "ŵ"}
        //, {'x', ""}
        , {'y', "ŷ"}
        , {'z', "źżž"}
    };
2
soslo

Je sais que ce n'est pas élégant, mais quand assembler ne fonctionne pas et que vous ne pouvez pas utiliser les fonctions, vous pouvez utiliser cette option imbriquée pour la plupart des signes diacritiques. Je poste ceci afin que vous n'ayez pas à taper à nouveau vous-même :)

select REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(myfield,'é','e'),'ê','e'),'ë','e'),'è','e'),'É','E'),'È','E'),'Ê','E'),'Ë','E'),'ð','D'),'Ð','D'),'â','a'),'à','a'),'á','a'),'ã','a'),'æ','a'),'à','a'),'å','a'),'Å','A'),'À','A'),'Á','A'),'Â','A'),'Ã','A'),'Æ','A'),'ä','a'),'Ä','A'),'ï','i'),'î','i'),'ì','i'),'í','i'),'Ì','I'),'Í','I'),'Î','I'),'Ï','I'),'ô','o'),'ò','o'),'ó','o'),'õ','o'),'ø','o'),'Ò','O'),'Ó','O'),'Ô','O'),'Õ','O'),'Ø','O'),'ö','o'),'Ö','O'),'û','u'),'ù','u'),'ú','u'),'Ù','U'),'Ú','U'),'Û','U'),'Ü','U'),'ü','u'),'ñ','n'),'Ñ','N'),'Ç','C'),'ç','c'),'ý','y'),'ÿ','y'),'Ý','Y'),'þ','T'),'Þ','t'),'ß','ss') from mytable
1
Gilbert

Avec deux tables auxiliaires dans votre schéma, vous pouvez transformer vos données en utilisant simplement une instruction SELECT.

Commençons par déclarer une vue pour mettre en oeuvre un tableau de nombres, compris entre 1 et 65536. La technique suivante est due à Itzik Ben-Gan :

CREATE VIEW Sequence
AS
WITH T1(_) AS (SELECT NULL UNION ALL SELECT NULL),
T2(_) AS (SELECT NULL FROM T1 AS L CROSS JOIN T1 AS R),
T3(_) AS (SELECT NULL FROM T2 AS L CROSS JOIN T2 AS R),
T4(_) AS (SELECT NULL FROM T3 AS L CROSS JOIN T3 AS R),
T5(_) AS (SELECT NULL FROM T4 AS L CROSS JOIN T4 AS R)
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS Number
FROM T5;

Ensuite, déclarez un mappage entre les caractères avec des signes diacritiques et leurs équivalents non coïncidents. Cet exemple de données n'est pas un mappage complet, mais sert d'exemple:

CREATE TABLE UndiacriticMap (
  DiacriticCharacter CHAR(1) NOT NULL PRIMARY KEY,
  UndiacriticCharacter CHAR(1) NOT NULL
);

INSERT INTO UndiacriticMap (
  DiacriticCharacter,
  UndiacriticCharacter
)
VALUES
  ('à', 'a'),
  ('è', 'e'),
  ('é', 'e'),
  ('ì', 'i'),
  ('ò', 'o'),
  ('ç', 'c');

Enfin, déclarez une table avec une colonne contenant des données de test. Les données proviennent de la question et de Réponse de DForck42 :

CREATE TABLE TestData (
  ID INT NOT NULL PRIMARY KEY,
  String VARCHAR(50) NOT NULL
);

INSERT INTO TestData (
  ID,
  String
)
VALUES
  (1, 'textàè containing éìòaccentsç''''` and things-'),
  (2, 'aèàç=.32s df');

Une fois ces objets en place, l'instruction SELECT suivante traite les données de test de manière relationnelle, à l'exception du hack non relationnel qui permet de concaténer les caractères. La 'méthode de la boîte noire XML' est due à Anith Sen :

WITH CharacterWise (ID, Symbol, Position) AS (
  SELECT
    ID,
    SUBSTRING(TestData.String, Sequence.Number, 1),
    Sequence.Number
  FROM TestData
  INNER JOIN Sequence ON
    Sequence.Number <= LEN(TestData.String)
),
Stripped (ID, Symbol, Position) AS (
  SELECT
    ID,
    CASE 
      WHEN UndiacriticMap.DiacriticCharacter IS NOT NULL
      THEN UndiacriticMap.UndiacriticCharacter
      ELSE CASE 
        WHEN CharacterWise.Symbol LIKE '[a-z]' COLLATE Latin1_General_BIN
        THEN CharacterWise.Symbol
        ELSE ''
      END
    END,
    CharacterWise.Position
  FROM CharacterWise
  LEFT OUTER JOIN UndiacriticMap ON
    UndiacriticMap.DiacriticCharacter = CharacterWise.Symbol
)
SELECT
  TestData.ID,
  TestData.String,
  (
    SELECT Stripped.Symbol AS [text()]
    FROM Stripped
    WHERE TestData.ID = Stripped.ID
    ORDER BY Stripped.Position
    FOR XML PATH('')
  ) AS StrippedString
FROM TestData;

La requête produit le jeu de résultats suivant:

ID          String                                             StrippedString
----------- -------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1           textàè containing éìòaccentsç''` and things-       textaecontainingeioaccentscandthings
2           aèàç=.32s df                                       aeacsdf

Juste deux cents de moi

select * From XXX  
    where cast(Word as varchar(max)) collate SQL_Latin1_General_CP1253_CI_AI = 'sluiten' collate SQL_Latin1_General_CP1253_CI_AI
0
Tschallacka

dans postgres 10:

create extension unaccent;# as root in Your db, for each db
select unaccent("ąęśłóŻŹŁÓĄĘ");

:)

0
blackmoon