2011-03-19 72 views
27

我有一个MySQL数据库和我有一个查询为:MySQL的 - 返回匹配模式的REGEXP查询

SELECT `id`, `originaltext` FROM `source` WHERE `originaltext` regexp '[0-9][0-9]' 

这个探测与在它2个位数编号的所有originaltexts。

我需要mysql返回这些数字作为字段,所以我可以进一步操纵它们。理想情况下,如果我可以添加额外的标准应该是> 20会很好,但我也可以单独做到这一点。

回答

12

如果您想在数据库中使用更多正则表达式,可以考虑使用LIB_MYSQLUDF_PREG。这是一个导入PCRE库的MySQL用户函数的开源库。 LIB_MYSQLUDF_PREG仅以源代码形式提供。要使用它,你需要能够编译它并将其安装到MySQL服务器中。安装这个库不会以任何方式改变MySQL的内置正则表达式支持。它仅提供以下附加功能:

PREG_CAPTURE从字符串中提取正则表达式匹配。 PREG_POSITION返回正则表达式与字符串匹配的位置。 PREG_REPLACE对字符串执行搜索和替换。 PREG_RLIKE测试一个正则表达式是否匹配一个字符串。

所有这些函数都将正则表达式作为它们的第一个参数。这个正则表达式必须像Perl正则表达式运算符那样格式化。例如。要测试正则表达式是否与主题不区分大小写匹配,可以使用MySQL代码PREG_RLIKE('/ regex/i',subject)。这与PHP的preg函数类似,这些函数还需要PHP字符串内正则表达式的额外//分隔符。

如果你想要更简单的东西,你可以改变这个功能,以更好地满足你的需求。

CREATE FUNCTION REGEXP_EXTRACT(string TEXT, exp TEXT) 
-- Extract the first longest string that matches the regular expression 
-- If the string is 'ABCD', check all strings and see what matches: 'ABCD', 'ABC', 'AB', 'A', 'BCD', 'BC', 'B', 'CD', 'C', 'D' 
-- It's not smart enough to handle things like (A)|(BCD) correctly in that it will return the whole string, not just the matching token. 

RETURNS TEXT 
DETERMINISTIC 
BEGIN 
    DECLARE s INT DEFAULT 1; 
    DECLARE e INT; 
    DECLARE adjustStart TINYINT DEFAULT 1; 
    DECLARE adjustEnd TINYINT DEFAULT 1; 

    -- Because REGEXP matches anywhere in the string, and we only want the part that matches, adjust the expression to add '^' and '$' 
    -- Of course, if those are already there, don't add them, but change the method of extraction accordingly. 

    IF LEFT(exp, 1) = '^' THEN 
    SET adjustStart = 0; 
    ELSE 
    SET exp = CONCAT('^', exp); 
    END IF; 

    IF RIGHT(exp, 1) = '$' THEN 
    SET adjustEnd = 0; 
    ELSE 
    SET exp = CONCAT(exp, '$'); 
    END IF; 

    -- Loop through the string, moving the end pointer back towards the start pointer, then advance the start pointer and repeat 
    -- Bail out of the loops early if the original expression started with '^' or ended with '$', since that means the pointers can't move 
    WHILE (s <= LENGTH(string)) DO 
    SET e = LENGTH(string); 
    WHILE (e >= s) DO 
     IF SUBSTRING(string, s, e) REGEXP exp THEN 
     RETURN SUBSTRING(string, s, e); 
     END IF; 
     IF adjustEnd THEN 
     SET e = e - 1; 
     ELSE 
     SET e = s - 1; -- ugh, such a hack to end it early 
     END IF; 
    END WHILE; 
    IF adjustStart THEN 
     SET s = s + 1; 
    ELSE 
     SET s = LENGTH(string) + 1; -- ugh, such a hack to end it early 
    END IF; 
    END WHILE; 

    RETURN NULL; 

END 
+0

嗯 - 我不认为这是明显的在我原来的职位,但原文已经得到了大量的文字“绒毛”的周围数...我需要“解压”的个数出来的。并且可能有一个原始文本中有多个数字... – Steve 2011-03-19 10:24:21

+0

我已更新答案。 – Pentium10 2011-03-19 10:36:32

8

MySQL中没有任何使用正则表达式提取文本的语法。您可以使用REGEXP来标识包含两个连续数字的行,但要解压缩它们,您必须使用在这种情况下非常困难的普通字符串操作函数。

替代方案:

  • 从数据库中选择整个值,则使用客户端上的正则表达式。
  • 使用更好的支持SQL标准的不同数据库(可能不是我所知的选项)。那么你可以使用这个:SUBSTRING(originaltext from '%#[0-9]{2}#%' for '#')
+0

我同意。我的直觉是从数据库中获取整个字段,然后使用脚本将其与正则表达式进行比较并提取匹配。 – dgmdan 2012-02-01 15:55:42

2

我有同样的问题,这是我找到的解决方法(但它不会在所有情况下工作):

  • 使用LOCATE()找到开头和结尾部分字符串你wan't匹配
  • 使用MID()提取子之间...
  • 守正则表达式匹配只有你一定能找到一个匹配,其中行。
+7

这里的一个例子可能会有所帮助 – Geoff 2015-03-03 18:34:57

2

我用我的代码作为存储过程(函数),应该工作以提取任何数字在一个块中建立的数字。这是我更广泛的图书馆的一部分。

DELIMITER $$ 

-- 2013.04 [email protected] 
-- FindNumberInText("ab 234 95 cd", TRUE) => 234 
-- FindNumberInText("ab 234 95 cd", FALSE) => 95 

DROP FUNCTION IF EXISTS FindNumberInText$$ 
CREATE FUNCTION FindNumberInText(_input VARCHAR(64), _fromLeft BOOLEAN) RETURNS VARCHAR(32) 
BEGIN 
    DECLARE _r    VARCHAR(32) DEFAULT ''; 
    DECLARE _i    INTEGER DEFAULT 1; 
    DECLARE _start   INTEGER DEFAULT 0; 
    DECLARE _IsCharNumeric BOOLEAN; 

    IF NOT _fromLeft THEN SET _input = REVERSE(_input); END IF; 
    _loop: REPEAT 
    SET _IsCharNumeric = LOCATE(MID(_input, _i, 1), "") > 0; 
    IF _IsCharNumeric THEN 
     IF _start = 0 THEN SET _start = _i; END IF; 
    ELSE 
     IF _start > 0 THEN LEAVE _loop;  END IF; 
    END IF; 
    SET _i = _i + 1; 
    UNTIL _i > length(_input) END REPEAT; 

    IF _start > 0 THEN 
    SET _r = MID(_input, _start, _i - _start); 
    IF NOT _fromLeft THEN SET _r = REVERSE(_r); END IF; 
    END IF; 
    RETURN _r; 
END$$ 
0

如果你想返回一个字符串的一部分:

SELECT id , substring(columnName,(locate('partOfString',columnName)),10) from tableName; 

Locate()将返回匹配字符串的开头现在的位置成为首发的Function Substring()

0

位置我知道这是相当一段时间以来,这个问题被问到,但遇到它,并认为这将是我的自定义正则表达式替代品一个很好的挑战 - 见this blog post

...好消息是它可以,但它需要被称为不少次。请参阅this online rextester demo,其中显示了下面SQL的工作方式。

SELECT reg_replace(
     reg_replace(
      reg_replace(
      reg_replace(
       reg_replace(
       reg_replace(
        reg_replace(txt, 
           '[^0-9]+', 
           ',', 
           TRUE, 
           1, -- Min match length 
           0 -- No max match length 
           ), 
          '([0-9]{3,}|,[0-9],)', 
          '', 
          TRUE, 
          1, -- Min match length 
          0 -- No max match length 
          ), 
          '^[0-9],', 
          '', 
          TRUE, 
          1, -- Min match length 
          0 -- No max match length 
          ), 
         ',[0-9]$', 
         '', 
         TRUE, 
         1, -- Min match length 
         0 -- No max match length 
         ), 
         ',{2,}', 
         ',', 
         TRUE, 
         1, -- Min match length 
         0 -- No max match length 
         ), 
        '^,', 
        '', 
        TRUE, 
        1, -- Min match length 
        0 -- No max match length 
        ), 
        ',$', 
        '', 
        TRUE, 
        1, -- Min match length 
        0 -- No max match length 
        ) AS `csv` 
FROM tbl;