MySQL剥离非数字字符进行比较

时间:2008-11-13 14:43:21

标签: mysql regex

我希望在表格中找到与用户输入的特定号码相匹配的记录。因此,用户可以输入12345,但这可能是数据库中的123zz4-5。

如果PHP函数在MySQL中有效,我想像这样的东西会起作用。

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

只用MySQL做同样的功能或方法是什么?

15 个答案:

答案 0 :(得分:37)

我意识到这是一个古老的话题,但谷歌搜索这个问题我找不到一个简单的解决方案(我看到了古老的代理人,但认为这是一个更简单的解决方案)所以这是我写的一个函数,似乎工作得很好

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
$$

答案 1 :(得分:7)

虽然它不漂亮且显示的结果不匹配,但这有助于:

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

我仍然希望找到一个类似于原始问题项目的更好的解决方案。

答案 2 :(得分:7)

没有regexp替换,只有一个普通的字符串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版本,但更准确地匹配。两者都表现同样糟糕,需要没有索引的全表扫描。

答案 3 :(得分:6)

最受欢迎的答案(@ 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

答案 4 :(得分:3)

我能想到的最简单的方法是使用MySQL REGEXP运算符la:

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

它并不是特别漂亮,但MySQL没有preg_replace功能,所以我认为这是你最好的。

就个人而言,如果这个唯一的数字数据如此重要,我会保留一个单独的字段来包含剥离的数据。它会使您的查找速度比使用正则表达式搜索快得多。

答案 5 :(得分:3)

此博客文章详细介绍了如何通过MySQL函数从字符串中删除非数字字符:

SELECT NumericOnly("asdf11asf");

返回11

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

答案 6 :(得分:3)

您可以使用REGEXP_REPLACE轻松做到您想要的(与MySQL 8+和MariaDB 10.0.5 +兼容)

  

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

     

将字符串expr中与模式pat指定的正则表达式匹配的匹配项替换为替换字符串repl,并返回结果字符串。如果expr,pat或repl为NULL,则返回值为NULL。

     

转到REGEXP_REPLACE doc:MySQLMariaDB

试一试:

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

<强>输出:

12312333

答案 7 :(得分:1)

我有类似的情况,将产品与条形码匹配,条形码有时不会存储任何字母数字,因此在搜索1022234时需要找到数据库中的102.2234。

最后,我刚刚在products表中添加了一个新字段reference_number,并且每当添加新产品时,php都会删除product_number中的无alpha数字以填充reference_number。

您需要对表进行一次扫描,以便为现有产品创建所有reference_number字段。

然后您可以设置索引,即使速度不是此操作的一个因素,保持数据库运行良好仍然是一个好主意,因此此查询不会使其陷入困境并减慢其他查询。

答案 8 :(得分:1)

我遇到了这个解决方案。 user1467716的最佳答案将在phpMyAdmin中进行一些小改动:在代码末尾添加第二个分隔符标记。

phpMyAdmin版本是4.1.14; MySQL版本5.6.20

我还使用

添加了长度限制器 声明中的

DECLARE count INT DEFAULT 0;

AND count < 5声明

中的

WHILE 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

答案 9 :(得分:0)

就我而言,没有正则表达式取代,但我找到了这个解决方案;

--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

此外,我发现此place可能有所帮助。

答案 10 :(得分:0)

foo的桌子有多大?如果它很小,并且速度确实无关紧要,你可以拉出行ID和foo,使用PHP替换函数进行循环比较,然后按行号拉出你想要的信息。

当然,如果桌子太大,这将无法正常工作。

答案 11 :(得分:0)

试试这个例子。这用于电话号码,但您可以根据需要进行修改。

   -- 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

答案 12 :(得分:0)

自从我从这里开始构建函数以来,我想分享一下。我只是重新排列了一下,以便于阅读(我只是服务器端)。

您可以通过传入表名和列名来调用它,以使其删除该列中所有现有的非数字字符。我继承了很多错误的表结构,这些结构将大量的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 ;

答案 13 :(得分:0)

要搜索与字符串中的特定数字模式匹配的数字,请按照以下类似方式首先删除所有字母和特殊字符,然后将值转换为整数,然后搜索

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

答案 14 :(得分:-1)

在MySQL 8.0+上,有一个新的本机函数,称为REGEXP_REPLACE。一个解决该问题的方法是:

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