2008-11-13 30 views
22

Estoy buscando encontrar registros en una tabla que coincida con un número específico que el usuario ingrese. Entonces, el usuario puede ingresar 12345, pero podría ser 123zz4-5 en la base de datos.MySQL tira caracteres no numéricos para comparar

Imagino que algo así funcionaría si las funciones de PHP funcionaran en MySQL.

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

¿Cuál es la función equivalente o manera de hacer esto con sólo MySQL?

Respuesta

6

Si bien no es bastante y muestra resultados que no coinciden, esto ayuda:

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

todavía me gustaría encontrar una mejor solución similar al elemento en la pregunta original.

3

La forma más simple que puedo pensar de hacerlo es utilizar el operador MySQL REGEXP a la:

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

No es especialmente bonita, pero MySQL no tienen una función preg_replace así que creo que es lo mejor que se va a poner

Personalmente, si estos datos numéricos son tan importantes, mantendría un campo separado solo para contener los datos eliminados. Hará tus búsquedas mucho más rápidas que con la búsqueda de expresiones regulares.

+0

La velocidad no es importante.Esto es para una herramienta de fondo que solo se usará cuando un elemento en la base de datos no se pueda encontrar de otra manera. –

+1

Eso no funciona en MySQL. –

+0

+1 para la sugerencia de agregar un campo para almacenar una versión normalizada (es decir, solo dígitos) del valor. –

0

No hay reemplazo de expresiones regulares en lo que a mí respecta, pero encontré esta solución;

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

funciona para mí y lo utilizan MySQL 5.0

También encontré este place que podría ayudar.

0

¿Qué tan grande es la mesa con foo? Si es pequeño, y la velocidad realmente no importa, puede extraer el ID de la fila y el foo, recorrerlo usando las funciones de reemplazo de PHP para comparar, y luego extraer la información que desea por número de fila.

Por supuesto, si la tabla es demasiado grande, esto no funcionará bien.

+0

Esto es probablemente más o menos la misma velocidad que mi ejemplo usando LIKE con comodines entre cada número. –

7

No hay reemplazo regexp, solo una cadena simple REEMPLAZAR().

MySQL tiene el operador REGEXP, pero es sólo un probador de partido no es un sustituto, por lo que tendría que convertir la lógica de dentro a fuera:

SELECT * FROM foo WHERE bar REGEXP '[^0-9]*1[^0-9]*2[^0-9]*3[^0-9]*4[^0-9]*5[^0-9]*'; 

Esto es como la versión con LIKE, pero si con más precisión . Ambos funcionarán igual de mal, y necesitan un escaneo completo de tabla sin índices.

1

que tienen una situación similar, que los productos a los códigos de barras en el que el código de barras no almacena ninguna numéricos alfa veces, por lo 102.2234 en el PP necesita ser encontrado en la búsqueda de 1022234.

Al final me acaba de agregar un nuevo campo, reference_number para las tablas de productos, y php strip out los none alfanuméricos en el número de producto para rellenar reference_number siempre que se agregue un producto nuevo.

Tendría que hacer una exploración de una sola vez de la tabla para crear todos los campos reference_number para los productos existentes.

Puede configurar su índice, incluso si la velocidad no es un factor para esta operación, sigue siendo una buena idea mantener la base de datos ejecutándose bien para que esta consulta no lo atasque y ralentice otras consultas.

30

que se dan cuenta de que esto es una antigua pero al buscar en Google este problema no pude encontrar una solución simple (vi a los venerables agentes pero creo que esta es una solución más simple) así que aquí hay una función que escribí, parece que funciona k bastante bien

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

¿Llamaría SUBSTRING() una vez para hacer esto más rápido? – Stoutie

+0

nm, funciona bastante rápido. Más de 100.000 registros en un segundo o dos. – Stoutie

+0

simplemente genial, gracias – Novasol

1

Encontré esta solución. La respuesta principal de user1467716 funcionará en phpMyAdmin con un pequeño cambio: agregue una segunda etiqueta delimitadora al final del código.

La versión de phpMyAdmin es 4.1.14; MySQL versión 5.6.20

I también añadió un limitador de longitud usando

DECLARE count INT DEFAULT 0; en las declaraciones

AND count < 5 en el estado WHILE

SET COUNT=COUNT+1; en el IF declaración

forma final:

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 
3

La respuesta más votada (@ user1467716) no es la más rápida. ¡Felicitaciones a todos por dar una propuesta de trabajo para rebotar!

Ésta es una versión mejorada:

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

Pruebas de 5000 veces en un servidor de prueba:

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

probar este ejemplo. esto se usa para números de teléfono, sin embargo, puede modificarlo para sus necesidades.

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

Para utilizar esta función definida por el usuario (UDF). digamos que tiene una columna de números de teléfono:

col1 
(513)983-3983 
1-838-338-9898 
phone983-889-8383 

select remove_alpha(col1) from mytable 

El resultado sería;

5139833983 
18383389898 
9838898383 
Cuestiones relacionadas