web-dev-qa-db-ja.com

MySQLの文字列からすべての非英数字を削除するにはどうすればよいですか?

文字列を比較するルーチンに取り組んでいますが、効率を上げるために、文字でも数字でもないすべての文字を削除する必要があります。

現在、複数のREPLACE関数を使用していますが、より高速で優れたソリューションがあるかもしれません。

55
Dylan

これらの答えはどれも私にとってはうまくいきませんでした。 charnumを削除する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)

ほら!

81

パフォーマンスの観点から(そして、あなたが書くよりも読むことを前提に)

最良の方法は、列のストリップバージョンを事前に計算して保存することだと思います。この方法では、変換の実行が少なくなります。

次に、新しい列にインデックスを配置し、データベースに作業を任せることができます。

20
Kevin Burton
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

13
Johan

ラテン文字とキリル文字のストレートでバトルテスト済みのソリューション:

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-привет ""]')
6
userlond

Ryan Shillingtonによる回答 に基づいて、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 ;
6
Alon Asulin

私が見つけることができた(そして使用した)最速の方法は、 convert()。

doc。からUSINGを指定したCONVERT()は、異なる文字セット間でデータを変換するために使用されます。

例:

_convert(string USING ascii)
_

あなたの場合、正しい文字セット自己定義

文書からの注。 [〜#〜] using [〜#〜]CONVERT()の形式は、 4.1.

5
vdd

MySQLでは、「」や「"」などの文字はアルファと見なされることに注意してください。次のようなものを使用することをお勧めします。

「a」と「z」の間のc OR c「A」と「Z」の間OR c「0」と「9」の間OR c = '-'その後

4
Abdel

この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拡張機能のインストールが必要です。

これは、私が 別の回答 に投稿し、 here についてブログに書いた正規表現置換関数を使用して実行できます。それは可能な限り最も効率的な解決策ではないかもしれず、手元の仕事には過剰すぎるように見えるかもしれませんが、スイスアーミーナイフのように、他の理由で役に立つかもしれません。

このRextesterオンラインデモ の英数字以外の文字をすべて削除するアクションで見ることができます。

SQL(簡潔にするために機能コードを除く)

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

これまでのところ、ここでの他の答えよりも複雑でない唯一の代替アプローチは、列の特殊文字の完全なセット、つまり、その列で現在使用されているすべての特殊文字を決定し、これらのすべてのキャラクター、例えば.

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

これはonly既知のデータセットに対して推奨されます。そうでない場合、一部の特殊文字がホワイトリストアプローチではなくブラックリストアプローチをすり抜けるのは簡単です。

明らかに、最も単純な方法は、堅牢な組み込みのホワイトリストがないために、SQLの外部でデータを事前検証することです(たとえば、正規表現の置換による)。

1
Kzqai

私たちのデータベースの姓とわずかに異なる名前を一致させようとすると、同様の問題が発生しました。たとえば、「McDonald」と「Mc Donald」、または「St John」と「St. John」として同じ人の名前を入力する場合がありました。

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';
1
lfjeff

プロシージャで文字列のアルファベット文字のみを取得する必要がありました。

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

英数字以外の文字を置き換えるが必要です。英数字以外の文字を削除するのではなく、Ryan Shillingtonの英数字に基づいて作成しました。最大255文字の文字列に対応

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 ; 

例:

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

必要な場合は、alphanum関数を個別に追加する必要があります。ここでは、例のためにこれを用意しています。

0
Luke Rehmann

いくつかのソリューションを試しましたが、最後にreplaceを使用しました。私のデータセットは部品番号であり、何を期待すべきかをかなり知っています。しかし、念のために、PHPを使用して長いクエリを作成しました。

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

これは、私が頭痛を起こすために使用したものを出力します:

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

pHPを使用している場合は...

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