MySQL将非数字字符进行比较
我正在寻找与表中匹配用户input的特定号码的logging。 因此,用户可以input12345,但是在数据库中可以是123zz4-5。
我想像这样的东西可以工作,如果PHP函数在MySQL中工作。
SELECT * FROM foo WHERE preg_replace("/[^0-9]/","",bar) = '12345'
什么是只有MySQL才能做到这一点的等价函数或方法?
我意识到这是一个古老的话题,但是在Google上search这个问题后,我找不到一个简单的解决scheme(我看到了这个可敬的代理商,但认为这是一个更简单的解决scheme),所以这里是我写的一个函数,似乎工作得很好。
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 $$
没有正则expression式replace,只有一个普通的stringREPLACE()。
MySQL有REGEXP运算符,但它只是一个匹配testing程序而不是代替程序,所以你必须把逻辑从头到尾:
SELECT * FROM foo WHERE bar REGEXP '[^0-9]*1[^0-9]*2[^0-9]*3[^0-9]*4[^0-9]*5[^0-9]*';
这就像你喜欢的版本,但更准确地匹配。 两者都会performance同样糟糕,需要一个没有索引的全表扫描。
虽然这不是很好,它显示的结果不匹配,这有助于:
SELECT * FROM foo WHERE bar LIKE = '%1%2%3%4%5%'
我仍然想find类似于原始问题中的项目的更好的解决scheme。
我能想到的最简单的方法就是使用MySQL REGEXP运算符la:
WHERE foo LIKE '1\D*2\D*3\D*4\D*5'
这不是特别漂亮,但MySQL没有preg_replace
函数,所以我认为这是最好的。
就个人而言,如果这个唯一的数字数据非常重要,我会保留一个单独的字段来包含剥离的数据。 这将使您的查询比正则expression式search快得多。
这篇博客文章详细介绍了如何通过MySQL函数从string中去除非数字字符:
SELECT NumericOnly("asdf11asf");
返回11
http://venerableagents.wordpress.com/2011/01/29/mysql-numeric-functions/
大多数upvoted的答案(@ user1467716)是不是最快的。 充分感谢他们给出一个工作build议,反弹!
这是一个改进版本:
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;;
在testing服务器上testing5000次:
-- 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
我也有类似的情况,将产品与条形码匹配,条形码有时不存储任何字母数字,因此在search1022234时需要find数据库中的102.2234。
最后,我只是在产品表中添加了一个新字段reference_number,并且在添加新产品时,php会删除product_number中的非alpha数字来填充reference_number。
您需要对表格执行一次扫描,以便为现有产品创build所有reference_number字段。
然后,您可以设置您的索引,即使速度不是这个操作的一个因素,保持数据库的正常运行仍然是一个好主意,所以这个查询不会妨碍其他查询。
我遇到了这个解决scheme。 user1467716最常见的答案将在phpMyAdmin中有一个小的改变:在代码的末尾添加第二个分隔符。
phpMyAdmin版本是4.1.14; MySQL版本5.6.20
我还加了一个长度限制器
DECLARE count INT DEFAULT 0;
在声明中
AND count < 5
在WHILE
语句中AND count < 5
SET COUNT=COUNT+1;
在IF
声明中
最终forms:
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
就我而言,没有正则expression式replace,但是我find了这个解决scheme。
--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
我还发现这个地方可以帮助。
用foo表有多大? 如果它很小,速度真的没有关系,你可以拉行ID和foo,使用PHPreplace函数进行比较,然后通过行号拉取所需的信息。
当然,如果桌子太大,这将不会很好。
试试这个例子。 这是用于电话号码,但是你可以修改它的需要。
-- 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