web-dev-qa-db-ja.com

MySQLは比較する非数値文字を取り除きます

ユーザーが入力した特定の番号に一致するテーブル内のレコードを探しています。したがって、ユーザーは12345と入力できますが、データベースでは123zz4-5となる可能性があります。

PHP関数がMySQLで機能した場合、このようなものが機能すると思います。

SELECT * FROM foo WHERE preg_replace("/[^0-9]/","",bar) = '12345'

MySQLだけでこれを行うための同等の機能または方法は何ですか?

36
Chris Bartow

きれいではなく、一致しない結果が表示されますが、これは役立ちます。

SELECT * FROM foo WHERE bar LIKE = '%1%2%3%4%5%'

元の質問の項目と同様のより良い解決策を見つけたいと思っています。

6
Chris Bartow

これは古代のトピックであることはわかっていますが、この問題をグーグルで調べたとき、簡単な解決策を見つけることができませんでした(私は古くからのエージェントを見ましたが、これはより簡単な解決策だと思います)。

DROP FUNCTION IF EXISTS STRIP_NON_DIGIT;
DELIMITER $$
CREATE FUNCTION STRIP_NON_DIGIT(input VARCHAR(255))
   RETURNS VARCHAR(255)
BEGIN
   DECLARE output   VARCHAR(255) 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' ) THEN
         SET output = CONCAT(output, SUBSTRING(input, iterator, 1));
      END IF;
      SET iterator = iterator + 1;
   END WHILE;
   RETURN output;
END
$$
41
user1467716

最も賛成された 上記の回答 は最速ではありません。
跳ね返る実用的な提案をしたことに対する彼らへの完全な称賛!

これは改良版です:

DELIMITER ;;
DROP FUNCTION IF EXISTS `STRIP_NON_DIGIT`;;

CREATE DEFINER=`root`@`localhost` FUNCTION `STRIP_NON_DIGIT`(input VARCHAR(255)) RETURNS VARCHAR(255) CHARSET utf8
READS SQL DATA
BEGIN
   DECLARE output    VARCHAR(255) DEFAULT '';
   DECLARE iterator  INT          DEFAULT 1;
   DECLARE lastDigit INT          DEFAULT 1;
   DECLARE len       INT;

   SET len = LENGTH(input) + 1;
   WHILE iterator < len DO
      -- skip past all digits
      SET lastDigit = iterator;
      WHILE ORD(SUBSTRING(input, iterator, 1)) BETWEEN 48 AND 57 AND iterator < len DO
         SET iterator = iterator + 1;
      END WHILE;

      IF iterator != lastDigit THEN
         SET output = CONCAT(output, SUBSTRING(input, lastDigit, iterator - lastDigit));
      END IF;

      WHILE ORD(SUBSTRING(input, iterator, 1)) NOT BETWEEN 48 AND 57 AND iterator < len DO
         SET iterator = iterator + 1;
      END WHILE;
   END WHILE;

   RETURN output;
END;;

テストサーバーで5000回のテスト:

-- original
Execution Time : 7.389 sec
Execution Time : 7.257 sec
Execution Time : 7.506 sec

-- ORD between not string IN
Execution Time : 4.031 sec

-- With less substrings
Execution Time : 3.243 sec
Execution Time : 3.415 sec
Execution Time : 2.848 sec
11
wally

正規表現による置換はなく、プレーンな文字列REPLACE()のみです。

MySQLにはREGEXP演算子がありますが、これは単なる一致テスターであり、置換子ではないため、ロジックを裏返しにする必要があります。

SELECT * FROM foo WHERE bar REGEXP '[^0-9]*1[^0-9]*2[^0-9]*3[^0-9]*4[^0-9]*5[^0-9]*';

これはLIKEを使用したバージョンに似ていますが、より正確に一致します。どちらもパフォーマンスは同じでなく、インデックスなしで全表スキャンが必要です。

7
bobince

_REGEXP_REPLACE_(MySQL 8+およびMariaDB 10.0.5+と互換性があります)を使用すると、簡単に好きなことができます

REGEXP_REPLACE(expr, pat, repl[, pos[, occurrence[, match_type]]])

パターンpatで指定された正規表現に一致する文字列expr内の出現箇所を置換文字列replで置き換え、結果の文字列を返します。 expr、pat、またはreplがNULLの場合、戻り値はNULLです。

REGEXP_REPLACEドキュメントに移動します: MySQL または MariaDB

試してみてください:

_SELECT REGEXP_REPLACE('123asd12333', '[a-zA-Z]+', '');
_

出力:

_12312333
_
6
Marlom

このブログ投稿では、MySQL関数を介して文字列から数値以外の文字を削除する方法について詳しく説明しています。

SELECT NumericOnly("asdf11asf");

戻り値 11

http://venerableagents.wordpress.com/2011/01/29/mysql-numeric-functions/

3
Jeremy Warne

私が考えることができる最も簡単な方法は、MySQL REGEXP演算子a laを使用することです。

WHERE foo LIKE '1\D*2\D*3\D*4\D*5'

特にきれいではありませんが、MySQLにはpreg_replace関数なので、取得するのに最適です。

個人的には、この数値のみのデータが非常に重要である場合、除去されたデータを含めるためだけに別のフィールドを保持します。これにより、正規表現検索よりもはるかに高速に検索を行うことができます。

3
Gareth

私も同様の状況で、製品をバーコードと照合し、バーコードが英数字をまったく保存しないことがあるので、1022234を検索するときにDB内の102.2234を見つける必要があります。

最後に、製品フィールドにreference_numberという新しいフィールドを追加しました。新しい製品が追加されるたびに、phpでproduct_numberの英数字を削除してreference_numberに入力します。

既存の製品のすべてのreference_numberフィールドを作成するには、テーブルを1回スキャンする必要があります。

その後、インデックスを設定できます。速度がこの操作の要素ではない場合でも、データベースを適切に実行しておくと、このクエリによってパフォーマンスが低下したり、他のクエリの速度が低下したりすることがなくなります。

1
user396149

私はこの解決策に出くわしました。 user1467716による上位の回答は、phpMyAdminで小さな変更を加えて機能します。コードの最後に2つ目の区切りタグを追加します。

phpMyAdminのバージョンは4.1.14です。 MySQLバージョン5.6.20

を使用して長さリミッターも追加しました

DECLARE count INT DEFAULT 0;宣言内

AND count < 5WHILEステートメント

SET COUNT=COUNT+1;IFステートメント

最終フォーム:

DROP FUNCTION IF EXISTS STRIP_NON_DIGIT;
DELIMITER $$
CREATE FUNCTION STRIP_NON_DIGIT(input VARCHAR(255))
   RETURNS VARCHAR(255)
BEGIN
   DECLARE output   VARCHAR(255) DEFAULT '';
   DECLARE iterator INT          DEFAULT 1;
   DECLARE count INT DEFAULT 0;
   WHILE iterator < (LENGTH(input) + 1) AND count < 5 DO --limits to 5 chars
      IF SUBSTRING(input, iterator, 1) IN ( '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ) THEN
         SET output = CONCAT(output, SUBSTRING(input, iterator, 1));
         SET COUNT=COUNT+1;
      END IF;
      SET iterator = iterator + 1;
   END WHILE;
   RETURN output;
END
$$
DELIMITER $$ --added this
1
modle13

私に関する限り、正規表現に代わるものはありませんが、この解決策を見つけました。

--Create a table with numbers
DROP TABLE IF EXISTS ints;
CREATE TABLE ints (i INT UNSIGNED NOT NULL PRIMARY KEY);

INSERT INTO ints (i) VALUES
( 1), ( 2), ( 3), ( 4), ( 5), ( 6), ( 7), ( 8), ( 9), (10),
(11), (12), (13), (14), (15), (16), (17), (18), (19), (20);

--Then extract the numbers from the specified column
SELECT
    bar,
    GROUP_CONCAT(SUBSTRING(bar, i, 1) ORDER BY i SEPARATOR '')
FROM foo
JOIN ints ON i BETWEEN 1 AND LENGTH(bar)
WHERE
    SUBSTRING(bar, i, 1) IN ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')
GROUP BY bar;

それは私のために働き、私はMySQL 5.0を使用します

また、私はこれを見つけることができます 場所 それは役立つかもしれません。

0
Nelson Miranda

Fooを含むテーブルの大きさは?それが小さく、速度が本当に重要でない場合は、行IDとfooをプルし、PHP replace関数を使用してループしてループし、必要な情報をプルすることができます。行番号。

もちろん、テーブルが大きすぎる場合、これはうまく機能しません。

0
Ben Doom

文字列内の特定の数値パターンに一致する数値を検索するには、まず以下のような方法ですべてのアルファベットと特殊文字を削除してから、値を整数に変換してから検索します

SELECT * 
FROM   foo 
WHERE  Convert(Regexp_replace(bar, '[a-zA-Z]+', ''), signed) = 12345 
0
Dr. Tom Kahigu

MySQL 8.0+では、REGEXP_REPLACEと呼ばれる新しいネイティブ関数があります。この質問に対する明確な解決策は次のとおりです。

SELECT * FROM foo WHERE REGEXP_REPLACE(bar,'[^0-9]+',"") = '12345'
0
Ícaro Mota

ここから機能を作り上げたので、これを共有しようと思いました。読みやすくするために並べ替えました(サーバー側です)。

これを呼び出すには、テーブル名と列名を渡して、その列から既存のすべての非数値文字を削除します。大量のintフィールドをvarcharとして配置する多くの不良なテーブル構造を継承したため、列を整数に変更する前にこれらをすばやくクリーンアップする方法が必要でした。

drop procedure if exists strip_non_numeric_characters;
DELIMITER ;;

CREATE PROCEDURE `strip_non_numeric_characters`(
    tablename varchar(100)
    ,columnname varchar(100)
    )
BEGIN

-- =============================================
-- Author:      <Author,,David Melton>
-- Create date: <Create Date,,2/26/2019>
-- Description: <Description,,loops through data and strips out the bad characters in whatever table and column you pass it>
-- =============================================

#this idea was generated from the idea STRIP_NON_DIGIT function
#https://stackoverflow.com/questions/287105/mysql-strip-non-numeric-characters-to-compare

declare input,output varchar(255);
declare iterator,lastDigit,len,counter int;
declare date_updated varchar(100);

select column_name 
    into date_updated
    from information_schema.columns 
    where table_schema = database() 
    and extra rlike 'on update CURRENT_TIMESTAMP'
    and table_name = tablename
    limit 1;

#only goes up to 255 so people don't run this for a longtext field
#just to be careful, i've excluded columns that are part of keys, that could potentially mess something else up
set @find_column_length = 
concat("select character_maximum_length
    into @len
    from information_schema.columns
    where table_schema = '",database(),"'
    and column_name = '",columnname,"'
    and table_name = '",tablename,"'
    and length(ifnull(character_maximum_length,100)) < 255
    and data_type in ('char','varchar')
    and column_key = '';");

prepare stmt from @find_column_length;
execute stmt;
deallocate prepare stmt;

set counter = 1;        
set len = @len;

while counter <= ifnull(len,1) DO

    #this just removes it by putting all the characters before and after the character i'm looking at
    #you have to start at the end of the field otherwise the lengths don't stay in order and you have to run it multiple times
    set @update_query = 
    concat("update `",tablename,"`
        set `",columnname,"` = concat(substring(`",columnname,"`,1,",len - counter,"),SUBSTRING(`",columnname,"`,",len - counter,",",counter - 1,"))
        ",if(date_updated is not null,concat(",`",date_updated,"` = `",date_updated,"`
        "),''),
        "where SUBSTRING(`",columnname,"`,",len - counter,", 1) not REGEXP '^[0-9]+$';");

    prepare stmt from @update_query;
    execute stmt;
    deallocate prepare stmt;

    set counter = counter + 1;

end while;

END ;;
DELIMITER ;
0
user11122383

この例を試してください。これは電話番号に使用されますが、必要に応じて変更できます。

   -- function removes non numberic characters from input
-- returne only the numbers in the string

CREATE DEFINER =`root`@`localhost` FUNCTION `remove_alpha`(inputPhoneNumber VARCHAR(50))
  RETURNS VARCHAR(50)
  CHARSET latin1
DETERMINISTIC
  BEGIN


    DECLARE inputLenght INT DEFAULT 0;
    -- var for our iteration 
    DECLARE counter INT DEFAULT 1;
    -- if null is passed, we still return an tempty string
    DECLARE sanitizedText VARCHAR(50) DEFAULT '';
    -- holder of each character during the iteration
    DECLARE oneChar VARCHAR(1) DEFAULT '';


    -- we'll process only if it is not null.
    IF NOT ISNULL(inputPhoneNumber)
    THEN
      SET inputLenght = LENGTH(inputPhoneNumber);
      WHILE counter <= inputLenght DO
        SET oneChar = SUBSTRING(inputPhoneNumber, counter, 1);
        IF (oneChar REGEXP ('^[0-9]+$'))
        THEN
          SET sanitizedText = Concat(sanitizedText, oneChar);
        END IF;

        SET counter = counter + 1;
      END WHILE;
    END IF;

    RETURN sanitizedText;
      END

このユーザー定義関数(UDF)を使用します。電話番号の列があるとしましょう:

col1
(513)983-3983
1-838-338-9898
phone983-889-8383

select remove_alpha(col1) from mytable

結果は次のようになります。

5139833983
18383389898
9838898383
0
Hugo R