Как удалить все не буквенно-цифровые символы из строки в MySQL? - PullRequest
55 голосов
/ 04 августа 2011

Я работаю над процедурой, которая сравнивает строки, но для большей эффективности мне нужно удалить все символы, которые не являются буквами или цифрами.

Я сейчас использую несколько REPLACE функций, но, может быть, есть более быстрое и приятное решение?

Ответы [ 17 ]

81 голосов
/ 07 апреля 2014

Ни один из этих ответов не работал для меня.Я должен был создать свою собственную функцию alphanum, которая разделила для меня символы:

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 ; 

Теперь я могу сделать:

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

и получаю:

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

Ура!

20 голосов
/ 04 августа 2011

С точки зрения производительности (и при условии, что вы читаете больше, чем пишете)

Я думаю, что лучшим способом было бы предварительно рассчитать и сохранить урезанную версию столбца, таким образомвы выполняете преобразование меньше.

Затем вы можете поместить индекс в новый столбец и получить базу данных, которая сделает всю работу за вас.

13 голосов
/ 04 августа 2011
SELECT teststring REGEXP '[[:alnum:]]+';

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

См .: http://dev.mysql.com/doc/refman/5.1/en/regexp.html
Прокрутите вниз до раздела, который говорит: [:character_class:]

Если вы хотите манипулировать строками, самый быстрый способ будет использоватьstr_udf, см .:
https://github.com/hholzgra/mysql-udf-regexp

6 голосов
/ 30 мая 2016

На основе ответа Райана Шиллингтона , модифицированного для работы со строками длиннее 255 символов и сохраняющих пробелы в исходной строке

К вашему сведению, в конце lower(str).

Я использовал это для сравнения строк:

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 ;
5 голосов
/ 19 марта 2014

Самый быстрый способ, который мне удалось найти (и использовать), - это convert ().

от Док. CONVERT () с USING используется для преобразования данных между различными наборами символов.

Пример:

convert(string USING ascii)

В вашем случае правильный набор символов будет самоопределяться

ПРИМЕЧАНИЕ от Док. Форма USING CONVERT() доступна с 4.1.0 .

4 голосов
/ 10 ноября 2016

Прямое и проверенное решение для латинских и кириллических символов:

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 ;

Использование:

-- outputs "hello12356"
SELECT remove_non_numeric_and_letters('hello - 12356-привет ""]')
4 голосов
/ 11 декабря 2013

Будьте осторожны, такие символы, как 'или », рассматриваются как альфа MySQL.Лучше использовать что-то вроде:

ЕСЛИ c МЕЖДУ 'a' И 'z' ИЛИ ​​c МЕЖДУ 'A' И 'Z' ИЛИ ​​c МЕЖДУ '0' И '9' ИЛИ ​​c = '- ТОГДА

4 голосов
/ 13 августа 2012

Я написал этот UDF.Тем не менее, он обрезает только специальные символы в начале строки.Он также преобразует строку в нижний регистр.При желании вы можете обновить эту функцию.

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');

Также вы можете использовать регулярные выражения, но для этого необходимо установить расширение MySql.

3 голосов
/ 05 августа 2016

Это можно сделать с помощью функции замены регулярных выражений, которую я опубликовал в другом ответе и написал в блоге о здесь . Возможно, это не самое эффективное решение, и может показаться излишним для этой работы, но, как швейцарский армейский нож, оно может пригодиться по другим причинам.

Это можно увидеть в действии, удалив все не алфавитно-цифровые символы в этой онлайн-демонстрации Rextester .

SQL (без краткости код функции) :

SELECT txt,
       reg_replace(txt,
                   '[^a-zA-Z0-9]+',
                   '',
                   TRUE,
                   0,
                   0
                   ) AS `reg_replaced`
FROM test;
1 голос
/ 15 апреля 2016

У меня была похожая проблема с попыткой сопоставить фамилии в нашей базе данных, которые немного отличались. Например, иногда люди вводили имя того же человека, что и «Макдональд», а также «Макдональд» или «Сент-Джон» и «Сент-Джон».

Вместо того, чтобы пытаться преобразовать данные Mysql, я решил эту проблему, создав функцию (в PHP), которая будет принимать строку и создавать регулярное выражение только для альфы:

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]*$';
}

Теперь я могу искать в базе данных запросом, подобным этому:

$lastname_regex = alpha_only_regex($lastname);
$query = "SELECT * FROM my_table WHERE lastname REGEXP '$lastname_regex';
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...