web-dev-qa-db-fra.com

Comment supprimer tous les caractères non alphanumériques d'une chaîne dans MySQL?

Je travaille sur une routine qui compare les chaînes, mais pour une meilleure efficacité, je dois supprimer tous les caractères qui ne sont ni des lettres ni des chiffres.

J'utilise plusieurs fonctions REPLACE maintenant, mais peut-être y a-t-il une solution plus rapide et plus agréable?

54
Dylan

Aucune de ces réponses n'a fonctionné pour moi. Je devais créer ma propre fonction appelée alphanum qui supprimait les caractères pour moi:

DROP FUNCTION IF EXISTS alphanum; 
DELIMITER | 
CREATE FUNCTION alphanum( str CHAR(255) ) RETURNS CHAR(255) DETERMINISTIC
BEGIN 
  DECLARE i, len SMALLINT DEFAULT 1; 
  DECLARE ret CHAR(255) DEFAULT ''; 
  DECLARE c CHAR(1); 
  SET len = CHAR_LENGTH( str ); 
  REPEAT 
    BEGIN 
      SET c = MID( str, i, 1 ); 
      IF c REGEXP '[[:alnum:]]' THEN 
        SET ret=CONCAT(ret,c); 
      END IF; 
      SET i = i + 1; 
    END; 
  UNTIL i > len END REPEAT; 
  RETURN ret; 
END | 
DELIMITER ; 

Maintenant je peux faire:

select 'This works finally!', alphanum('This works finally!');

et je reçois:

+---------------------+---------------------------------+
| This works finally! | alphanum('This works finally!') |
+---------------------+---------------------------------+
| This works finally! | Thisworksfinally                |
+---------------------+---------------------------------+
1 row in set (0.00 sec)

Hourra!

78
Ryan Shillington

Du point de vue de la performance, (Et en supposant que vous lisiez plus que vous n’écrivez)

Je pense que le meilleur moyen serait de pré-calculer et de stocker une version épurée de la colonne, Ainsi, vous transformerez moins.

Vous pouvez ensuite mettre un index sur la nouvelle colonne et demander à la base de données de faire le travail à votre place.

19
Kevin Burton
SELECT teststring REGEXP '[[:alnum:]]+';

SELECT * FROM testtable WHERE test REGEXP '[[:alnum:]]+'; 

Voir: http://dev.mysql.com/doc/refman/5.1/en/regexp.html
Faites défiler jusqu'à la section qui dit:[:character_class:]

Si vous voulez manipuler des chaînes, le moyen le plus rapide consiste à utiliser un str_udf, voir:
https://github.com/hholzgra/mysql-udf-regexp

13
Johan

Basé sur la réponse de Ryan Shillington , modifiée pour fonctionner avec des chaînes de plus de 255 caractères et en préservant les espaces de la chaîne d'origine. 

Pour info il y a lower(str) à la fin.

Je l'ai utilisé pour comparer des chaînes:

DROP FUNCTION IF EXISTS spacealphanum;
DELIMITER $$
CREATE FUNCTION `spacealphanum`( str TEXT ) RETURNS TEXT CHARSET utf8
BEGIN 
  DECLARE i, len SMALLINT DEFAULT 1; 
  DECLARE ret TEXT DEFAULT ''; 
  DECLARE c CHAR(1); 
  SET len = CHAR_LENGTH( str ); 
  REPEAT 
    BEGIN 
      SET c = MID( str, i, 1 ); 
      IF c REGEXP '[[:alnum:]]' THEN 
        SET ret=CONCAT(ret,c); 
      ELSEIF  c = ' ' THEN
          SET ret=CONCAT(ret," ");
      END IF; 
      SET i = i + 1; 
    END; 
  UNTIL i > len END REPEAT; 
  SET ret = lower(ret);
  RETURN ret; 
  END $$
  DELIMITER ;
6
Alon Asulin

Le moyen le plus rapide que j'ai pu trouver (et utiliser) est avec convert ().

de Doc. CONVERT () avec USING est utilisé pour convertir les données entre différents jeux de caractères.

Exemple:

convert(string USING ascii)

Dans votre cas, le jeu de caractères droit sera défini par vous-même

NOTE de Doc. Le formulaire UTILISANT de CONVERT() est disponible à partir de 4.1.0

5
vdd

Attention, les caractères tels que ’ou 'sont considérés comme alpha par MySQL . Il vaut mieux utiliser quelque chose comme:

SI c ENTRE 'a' ET 'z' OR c ENTRE 'A' ET 'Z' OR c ENTRE '0' ET '9' OR c = '-' ALORS

4
Abdel

J'ai écrit cette UDF. Cependant, seuls les caractères spéciaux au début de la chaîne sont supprimés. Il convertit également la chaîne en minuscule. Vous pouvez mettre à jour cette fonction si vous le souhaitez.

DELIMITER //

DROP FUNCTION IF EXISTS DELETE_DOUBLE_SPACES//

CREATE FUNCTION DELETE_DOUBLE_SPACES ( title VARCHAR(250) )
RETURNS VARCHAR(250) DETERMINISTIC
BEGIN
    DECLARE result VARCHAR(250);
    SET result = REPLACE( title, '  ', ' ' );
    WHILE (result <> title) DO 
        SET title = result;
        SET result = REPLACE( title, '  ', ' ' );
    END WHILE;
    RETURN result;
END//

DROP FUNCTION IF EXISTS LFILTER//

CREATE FUNCTION LFILTER ( title VARCHAR(250) )
RETURNS VARCHAR(250) DETERMINISTIC
BEGIN
    WHILE (1=1) DO
        IF(  ASCII(title) BETWEEN ASCII('a') AND ASCII('z')
            OR ASCII(title) BETWEEN ASCII('A') AND ASCII('Z')
            OR ASCII(title) BETWEEN ASCII('0') AND ASCII('9')
        ) THEN
            SET title = LOWER( title );
            SET title = REPLACE(
                REPLACE(
                    REPLACE(
                        title,
                        CHAR(10), ' '
                    ),
                    CHAR(13), ' '
                ) ,
                CHAR(9), ' '
            );
            SET title = DELETE_DOUBLE_SPACES( title );
            RETURN title;
        ELSE
            SET title = SUBSTRING( title, 2 );          
        END IF;
    END WHILE;
END//
DELIMITER ;

SELECT LFILTER(' !@#$%^&*()_+1a    b');

Vous pouvez également utiliser des expressions régulières, mais cela nécessite l'installation d'une extension MySql.

Solution simple et directe pour les caractères latins et cyrilliques:

DELIMITER //

CREATE FUNCTION `remove_non_numeric_and_letters`(input TEXT)
  RETURNS TEXT
  BEGIN
    DECLARE output TEXT DEFAULT '';
    DECLARE iterator INT DEFAULT 1;
    WHILE iterator < (LENGTH(input) + 1) DO
      IF SUBSTRING(input, iterator, 1) IN
         ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ж', 'З', 'И', 'Й', 'К', 'Л', 'М', 'Н', 'О', 'П', 'Р', 'С', 'Т', 'У', 'Ф', 'Х', 'Ц', 'Ч', 'Ш', 'Щ', 'Ъ', 'Ы', 'Ь', 'Э', 'Ю', 'Я', 'а', 'б', 'в', 'г', 'д', 'е', 'ж', 'з', 'и', 'й', 'к', 'л', 'м', 'н', 'о', 'п', 'р', 'с', 'т', 'у', 'ф', 'х', 'ц', 'ч', 'ш', 'щ', 'ъ', 'ы', 'ь', 'э', 'ю', 'я')
      THEN
        SET output = CONCAT(output, SUBSTRING(input, iterator, 1));
      END IF;
      SET iterator = iterator + 1;
    END WHILE;
    RETURN output;
  END //

DELIMITER ;

Usage:

-- outputs "hello12356"
SELECT remove_non_numeric_and_letters('hello - 12356-привет ""]')
3
userlond

Cela peut être fait avec une fonction de remplacement d'expression régulière que j'ai postée dans une autre réponse et que j'ai blogué à propos de ici . Ce n'est peut-être pas la solution la plus efficace possible et peut paraître exagéré pour le travail à accomplir - mais, à l'instar d'un couteau suisse, il peut s'avérer utile pour d'autres raisons.

Vous pouvez le voir en action supprimer tous les caractères non alphanumériques dans cette démonstration en ligne de Rextester .

SQL (à l'exception du code de fonction pour plus de concision)}:

SELECT txt,
       reg_replace(txt,
                   '[^a-zA-Z0-9]+',
                   '',
                   TRUE,
                   0,
                   0
                   ) AS `reg_replaced`
FROM test;
2
Steve Chambers

Jusqu'ici, la seule approche alternative moins compliquée que les autres réponses ici est de déterminer l'ensemble des caractères spéciaux de la colonne, c'est-à-dire tous les caractères spéciaux utilisés dans cette colonne pour le moment, puis d'effectuer un remplacement séquentiel de tous ces personnages, par exemple.

update pages set slug = lower(replace(replace(replace(replace(name, ' ', ''), '-', ''), '.', ''), '&', '')); # replacing just space, -, ., & only

.

Ceci est seulement recommandé sur un ensemble de données connu, sinon c'est trivial pour certains personnages spéciaux de glisser avec un approche liste noire au lieu d'une approche liste blanche.

De toute évidence, le moyen le plus simple est de valider à l'avance les données en dehors de SQL en raison de l'absence de liste blanche intégrée robuste (par exemple, via un remplacement de regex).

1
Kzqai

J'ai eu un problème similaire en essayant de faire correspondre des noms de famille légèrement différents dans notre base de données. Par exemple, il arrive que des personnes inscrivent le même nom de personne sous les noms "McDonald", ainsi que "Mc Donald" ou "St John" et "St. John".

Au lieu d'essayer de convertir les données Mysql, j'ai résolu le problème en créant une fonction (en PHP) qui prendrait une chaîne et créerait une expression régulière alpha uniquement:

function alpha_only_regex($str) {
    $alpha_only = str_split(preg_replace('/[^A-Z]/i', '', $str));
    return '^[^a-zA-Z]*'.implode('[^a-zA-Z]*', $alpha_only).'[^a-zA-Z]*$';
}

Maintenant, je peux chercher dans la base de données avec une requête comme celle-ci:

$lastname_regex = alpha_only_regex($lastname);
$query = "SELECT * FROM my_table WHERE lastname REGEXP '$lastname_regex';
1
lfjeff

Je devais obtenir uniquement les caractères alphabétiques d'une chaîne dans une procédure et j'ai:

SET @source = "whatever you want";
SET @target = '';
SET @i = 1;
SET @len = LENGTH(@source);
WHILE @i <= @len DO
    SET @char = SUBSTRING(@source, @i, 1);
    IF ((ORD(@char) >= 65 && ORD(@char) <= 90) || (ORD(@char) >= 97 && ORD(@char) <= 122)) THEN
        SET @target = CONCAT(@target, @char);
    END IF;
    SET @i = @i + 1;
END WHILE;
0
Alain Tiemblo

Nécessaire pour remplacer les caractères non alphanumériques plutôt que de supprimer les caractères non alphanumériques, j'ai donc créé ce modèle à partir de l'alphanum de Ryan Shillington. Fonctionne pour les chaînes d'une longueur maximale de 255 caractères

DROP FUNCTION IF EXISTS alphanumreplace; 
DELIMITER | 
CREATE FUNCTION alphanumreplace( str CHAR(255), d CHAR(32) ) RETURNS CHAR(255) 
BEGIN 
  DECLARE i, len SMALLINT DEFAULT 1; 
  DECLARE ret CHAR(32) DEFAULT ''; 
  DECLARE c CHAR(1); 
  SET len = CHAR_LENGTH( str ); 
  REPEAT 
    BEGIN 
      SET c = MID( str, i, 1 ); 
      IF c REGEXP '[[:alnum:]]' THEN SET ret=CONCAT(ret,c); 
      ELSE SET ret=CONCAT(ret,d);
      END IF; 
      SET i = i + 1; 
    END; 
  UNTIL i > len END REPEAT; 
  RETURN ret; 
END | 
DELIMITER ; 

Exemple:

select 'hello world!',alphanum('hello world!'),alphanumreplace('hello world!','-');
+--------------+--------------------------+-------------------------------------+
| hello world! | alphanum('hello world!') | alphanumreplace('hello world!','-') |
+--------------+--------------------------+-------------------------------------+
| hello world! | helloworld               | hello-world-                        |
+--------------+--------------------------+-------------------------------------+

Vous aurez besoin d'ajouter la fonction alphanum séparément si vous le souhaitez, je l'ai juste ici pour l'exemple.

0
Luke Rehmann

J'ai essayé quelques solutions, mais à la fin j'ai utilisé replace. Mon ensemble de données est composé de références et je sais à quoi m'attendre. Mais pour des raisons de santé mentale, j’ai utilisé PHP pour construire la requête longue:

$dirty = array(' ', '-', '.', ',', ':', '?', '/', '!', '&', '@');
$query = 'part_no';
foreach ($dirty as $dirt) {
    $query = "replace($query,'$dirt','')";
}
echo $query;

Cela génère quelque chose qui me donnait mal à la tête:

replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(part_no,' ',''),'-',''),'.',''),',',''),':',''),'?',''),'/',''),'!',''),'&',''),'@','')
0
Josef Habr

si vous utilisez php alors .... 

try{
$con = new PDO ("mysql:Host=localhost;dbname=dbasename","root","");
}
catch(PDOException $e){
echo "error".$e-getMessage();   
}

$select = $con->prepare("SELECT * FROM table");
$select->setFetchMode(PDO::FETCH_ASSOC);
$select->execute();

while($data=$select->fetch()){ 

$id = $data['id'];
$column = $data['column'];
$column = preg_replace("/[^a-zA-Z0-9]+/", " ", $column); //remove all special characters

$update = $con->prepare("UPDATE table SET column=:column WHERE id='$id'");
$update->bindParam(':column', $column );
$update->execute();

// echo $column."<br>";
} 
0
user7796548