匹配最好的正则表达式

时间:2011-03-08 19:34:36

标签: mysql sql regex match

在下表中,我如何匹配“FG2-4G4T5”,以便第一个返回的行是最佳匹配(可能是“FG2-4G”)?

part_match
^FG2|^FF2|^FF3-S|^FF4-GR
^FG2-4G
^FG2
 FG2-RGST

以下查询在这种情况下不起作用,因为“^FG2|^FF2|^FF3-S|^FF4-GR”的长度更长:

SELECT * FROM parts WHERE 'FG2-4G4T5' REGEXP part_match ORDER BY length(part_match) DEC

任何帮助将不胜感激。提前谢谢。

2 个答案:

答案 0 :(得分:2)

有时是K.I.S.S.解决方案最好。保留另一列,列出模式匹配的实际文本长度?

part_match           part_match_length
======================================
^FG2|^FF2|^FF3|^FF4  3
^FG2-4G              6
^FG2                 3

因而......

SELECT * FROM parts
WHERE 'FG2-4G4T5' REGEXP part_match
ORDER BY part_match_length DEC

从技术上讲,第二列甚至不一定必须是长度 - 只是指示给定模式提供的匹配有多好。

答案 1 :(得分:0)

假设你的part_match总是^ xxxx形式,并且可选择EQUAL-SIZED ^ yyyy | ^ zzzz | ...

SELECT * FROM parts
WHERE 'FG2-4G4T5' REGEXP part_match
ORDER BY instr(concat(part_match,'|'),'|') DEC

这将为您提供最长的匹配前缀。

要解决包含FULL匹配的问题,请从部分匹配的长度中选择一个,因此,

SELECT * FROM parts
WHERE 'FG2-4G4T5' REGEXP part_match
ORDER BY CASE WHEN part_match LIKE '^%' THEN -1 else 0 end +
         instr(concat(part_match,'|'),'|') DEC

解决涉及多个不等长度部分的REGEX,例如: ^YYYY|^Z|ABC,您首先需要找到一个将其分成多行的函数

part_match    | single_part
^YYYY|^Z|ABC    ^YYYY
^YYYY|^Z|ABC    ^Z
^YYYY|^Z|ABC    ABC

然后通过single_part的REGEXP引用回答中的part_match。这不是一件容易的事,但可以使用Numbers表并明智地使用SUBSTR和INSTR来实现。

创建一个数字表(运行一次):

DROP PROCEDURE IF EXISTS CreateNumbersTable;

delimiter //
CREATE PROCEDURE CreateNumbersTable()
    LANGUAGE SQL
    NOT DETERMINISTIC
    CONTAINS SQL
    SQL SECURITY DEFINER
    COMMENT ''
BEGIN
  drop table if exists Numbers;
  create table Numbers (N int primary key);

  SET @x := 0;
  REPEAT 
    insert into Numbers values (@x);
    SET @x := @x + 1;
    UNTIL @x > 999 END REPEAT;
END//
delimiter ;

CALL CreateNumbersTable;

DROP PROCEDURE CreateNumbersTable;

然后您可以使用此查询

select p.*
#  ,substr(p.part_match, N.N+1, locate('|', concat(p.part_match,'|'), N.N+2) -N.N -1)
#  ,length(substr(p.part_match, N.N+1, locate('|', concat(p.part_match,'|'), N.N+2) -N.N -1))
from parts p
inner join numbers N on N.N between 0 and length(p.part_match)
  and ((N.N = 0) or (substr(p.part_match, N.N, 1) = '|'))
WHERE 'FG2-RGST' REGEXP p.part_match
  and 'FG2-RGST' REGEXP substr(p.part_match, N.N+1, locate('|', concat(p.part_match,'|'), N.N+2) -N.N -1)
order by length(substr(p.part_match, N.N+1, locate('|', concat(p.part_match,'|'), N.N+2) -N.N -1)) DESC

取消注释第2行和第3行,以查看它匹配的part_match部分。