2011-03-19 11 views
27

tengo una base de datos MySQL y tengo una consulta como:MySQL - Volver coincidencia de patrones en REGEXP consulta

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

Este detecta todos originaltexts que tienen números con 2 dígitos en el mismo.

Necesito mysql para devolver esos números como campo, así que puedo manipularlos más.

Idealmente, si puedo agregar criterios adicionales que deberían ser> 20 sería genial, pero también puedo hacerlo por separado.

Respuesta

12

Si desea un poder de expresión más regular en su base de datos, puede considerar usar LIB_MYSQLUDF_PREG. Esta es una biblioteca de código abierto de funciones de usuario MySQL que importa la biblioteca PCRE. LIB_MYSQLUDF_PREG se entrega únicamente en formato de código fuente. Para usarlo, necesitarás poder compilarlo e instalarlo en tu servidor MySQL. Instalar esta biblioteca no cambia la compatibilidad con expresiones regulares incorporadas de MySQL de ninguna manera. Simplemente hace disponibles las siguientes funciones adicionales:

PREG_CAPTURE extrae una coincidencia de expresión regular de una cadena. PREG_POSITION devuelve la posición en la que una expresión regular coincide con una cadena. PREG_REPLACE realiza una búsqueda y reemplazo en una cadena. PREG_RLIKE prueba si una expresión regular coincide con una cadena.

Todas estas funciones toman una expresión regular como su primer parámetro. Esta expresión regular debe formatearse como un operador de expresión regular de Perl. P.ej. para probar si Regex coincide con el asunto insensiblemente, usaría el código PREG_RLIKE de MySQL ('/ regex/i', asunto). Esto es similar a las funciones de preg de PHP, que también requieren los delimitadores adicionales // para las expresiones regulares dentro de la cadena de PHP.

Si quiere algo más simple, puede modificar esta función para satisfacer mejor sus necesidades.

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

Hmm - No creo que estaba claro en mi post original, pero el texto original tiene una gran cantidad de texto 'pelusa' que rodea el número ... necesito "extraer" el número de ella . Y puede haber más de 1 número en un texto original ... – Steve

+0

He actualizado la respuesta. – Pentium10

8

No hay ninguna sintaxis en MySQL para extraer texto usando expresiones regulares. Puede usar el REGEXP para identificar las filas que contienen dos dígitos consecutivos, pero para extraerlos debe usar las funciones normales de manipulación de cadenas, lo cual es muy difícil en este caso.

Alternativas:

  • seleccionar el valor de la totalidad de la base de datos a continuación, utilizar una expresión regular en el cliente.
  • Utilice una base de datos diferente que tenga un mejor soporte para el estándar SQL (puede que no sea una opción, lo sé). Entonces puede usar esto: SUBSTRING(originaltext from '%#[0-9]{2}#%' for '#').
+0

Estoy de acuerdo. Mi instinto sería obtener todo el campo del DB, luego usar un script para compararlo con la expresión regular y extraer la coincidencia. – dgmdan

2

estoy teniendo el mismo problema, y ​​esta es la solución que encontré (pero no funcionará en todos los casos):

  • uso LOCATE() para encontrar el principio y el final de la cadena que wa no para que coincida con
  • uso MID() para extraer la subcadena en el medio ...
  • mantener la expresión regular para que coincida con sólo las filas en las que usted está seguro de encontrar una coincidencia.
+7

Un ejemplo aquí podría ayudar – Geoff

2

Utilicé mi código como un Procedimiento almacenado (Función), debo trabajar para extraer cualquier número construido a partir de dígitos en un solo bloque. Esta es una parte de mi biblioteca más amplia.

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

Si desea devolver una parte de una cadena:

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

Locate() devolverá el Postion de partida de la cadena coincidente, que se hace cargo del Function Substring()

0

partir sé que ha sido bastante Hace un tiempo desde que se hizo esta pregunta, pero me encontré con ella y pensé que sería un buen desafío para mi sustituto de expresiones regulares personalizado. Consulte this blog post.

... Y la buena noticia es que sí, aunque debe llamarse varias veces. Ver this online rextester demo, que muestra el funcionamiento que llegó al SQL a continuación.

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; 
Cuestiones relacionadas