2009-06-10 10 views
13

Tengo una tabla con dos columnas: price (int) y price_display (varchar).¿Cómo se extrae un valor numérico de una cadena en una consulta MySQL?

precio es el precio numérico real, p. "9990"

price_display es la representación visual, p. "$ 9.99" o "9.99Fr"

he podido confirmar las dos columnas coincide través de expresiones regulares:

price_display no expreg formato (precio/1000, 2)

Pero en el caso de una discrepancia, quiero extraer el valor de la columna price_display y configurarlo en la columna de precios, todo dentro del contexto de una declaración de actualización. No he podido averiguar cómo.

Gracias.

Respuesta

30

Esta función hace el trabajo de solamente volver los dígitos 0-9 de la cadena, que hace el trabajo muy bien para resolver el problema, independientemente de lo prefijos o postfixes que tienes.

http://www.artfulsoftware.com/infotree/queries.php?&bw=1280#815

copiado aquí por referencia:

SET GLOBAL log_bin_trust_function_creators=1; 
DROP FUNCTION IF EXISTS digits; 
DELIMITER | 
CREATE FUNCTION digits(str CHAR(32)) RETURNS CHAR(32) 
BEGIN 
    DECLARE i, len SMALLINT DEFAULT 1; 
    DECLARE ret CHAR(32) DEFAULT ''; 
    DECLARE c CHAR(1); 

    IF str IS NULL 
    THEN 
    RETURN ""; 
    END IF; 

    SET len = CHAR_LENGTH(str); 
    REPEAT 
    BEGIN 
     SET c = MID(str, i, 1); 
     IF c BETWEEN '0' AND '9' THEN 
     SET ret=CONCAT(ret,c); 
     END IF; 
     SET i = i + 1; 
    END; 
    UNTIL i > len END REPEAT; 
    RETURN ret; 
END | 
DELIMITER ; 

SELECT digits('$10.00Fr'); 
#returns 1000 
+0

Además de la solución anterior, en caso de que el número simplemente comience o simplemente termine con una letra o carácter (por ejemplo, $ 10.00 o 10.00Fr), existe una solución más simple y eficiente para extraer los números. – Angel

+0

Impresionante, gracias. Un pequeño consejo para otros lectores: si convierte 'IF c BETWEEN' en' IF c NOT BETWEEN', puede usar esta función para hacer coincidir cualquier carácter que sea ** no ** dígitos. – user2428118

0

Esto es un "código de horror", los esquemas de bases de datos relacionales deberían NO escribirse así!

Tiene que escribir código complejo e innecesario para validar los datos.

intentar algo como esto:

SELECT CONCAT('$',(price/1000)) AS Price FROM ... 

Además, se puede utilizar un float, double o real en lugar de un número entero.

Si necesita almacenar datos de moneda, puede considerar agregar un campo de moneda o usar las funciones de configuración regional del sistema para mostrarlo en el formato correcto.

+2

Gracias por la respuesta. Desafortunadamente no tengo control sobre el esquema; Simplemente lo heredé y ahora debo corregir los datos contenidos. –

4

Un enfoque sería utilizar REPLACE() función:

UPDATE my_table 
SET price = replace(replace(replace(price_display,'Fr',''),'$',''),'.','') 
WHERE price_display not regexp format(price/1000, 2); 

Esto funciona para los datos que dio ejemplos:

'$9.99' 
'9.99Fr' 

Ambos resultan en 999 en mi prueba. Con una actualización como esta, es importante asegurarse primero de realizar una copia de seguridad de la base de datos y conocer los formatos de los elementos. Puede ver todos los "malos" al hacer esta consulta:

SELECT DISTINCT price_display 
FROM  my_table 
WHERE price_display not regexp format(price/1000, 2) 
ORDER BY price_display; 
+0

Gracias, esto es bueno, pero por supuesto tengo muchos más símbolos de moneda que solo "$" y "Fr", por lo que reemplazar la cadena estaría fuera de control. –

+0

Me interesaría ver algunos resultados representativos de la última consulta que publiqué. ¿Cuántos elementos distintos hay? Tal vez con algunos ejemplos más, yo u otra persona pueda hacer una pieza de código más informada. Una cosa en la que pensé es que si tiene elementos formateados como "$ 10" que representan $ 10.00, entonces mi método falla. Pero luego no publicaste eso como una posibilidad. La última consulta que publiqué sería útil para determinar la consulta que hará la magia. – artlung

1

puedo crear un procedimiento que detecta el primer número en una cadena y devuelva esta, si no devuelve 0.

DROP FUNCTION IF EXISTS extractNumber; 
    DELIMITER // 
    CREATE FUNCTION extractNumber (string1 VARCHAR(255)) RETURNS INT(11) 
     BEGIN 
     DECLARE position, result, longitude INT(11) DEFAULT 0; 
     DECLARE string2 VARCHAR(255); 
     SET longitude = LENGTH(string1); 
     SET result = CONVERT(string1, SIGNED); 
     IF result = 0 THEN 
      IF string1 REGEXP('[0-9]') THEN 
       SET position = 2; 
       checkString:WHILE position <= longitude DO 
        SET string2 = SUBSTR(string1 FROM position); 
        IF CONVERT(string2, SIGNED) != 0 THEN 
         SET result = CONVERT(string2, SIGNED); 
         LEAVE checkString; 
        END IF; 
        SET position = position + 1; 
       END WHILE; 
      END IF; 
     END IF; 
     RETURN result; 
    END // 
    DELIMITER ; 
4

Para mí CASTING el campo hizo el truco:

CAST(price AS UNSIGNED) // Por entero positivo

CAST(price AS SIGNED) // Por entero negativo y positivo

IF(CAST(price AS UNSIGNED)=0,REVERSE(CAST(REVERSE(price) AS UNSIGNED)),CAST(price AS UNSIGNED)) // Fijar cuando el precio comienza con algo más que un dígito

Para obtener más información, ver:

https://dev.mysql.com/doc/refman/5.0/en/cast-functions.html

+0

me encanta. Simplemente ejecuté un reverso en la cuerda, lo fundí y lo invirtió nuevamente para obtener el número al final de la cuerda. Simple ... Eficaz ... Confiable. gracias. – Barry

+0

Impresionante. Esto es ideal para dividir números de casas y personajes, ej. '' '40A''' en' '' 40''' y '' 'A''' como este: ' '' SELECT CAST (t.col AS UNSIGNED) como "Number", REPLACE (t.col) , CAST (t.col AS UNSIGNED), "") AS "Character" '' ' –

+0

No sé de qué están hablando. Este enfoque es defectuoso: ejecutar 'REVERSE (CAST (REVERSE (numstring) como UNSIGNED))' en un valor como 'string-10' devolverá' 1' y no '10'. – WoodrowShigeru

1

Volver último número de la cadena:

CREATE FUNCTION getLastNumber(str VARCHAR(255)) RETURNS INT(11) 
DELIMETER // 
BEGIN 
    DECLARE last_number, str_length, position INT(11) DEFAULT 0; 
    DECLARE temp_char VARCHAR(1); 
    DECLARE temp_char_before VARCHAR(1); 


IF str IS NULL THEN 
    RETURN -1; 
END IF; 

SET str_length = LENGTH(str); 

WHILE position <= str_length DO 
    SET temp_char = MID(str, position, 1); 

    IF position > 0 THEN 
     SET temp_char_before = MID(str, position - 1, 1); 
    END IF; 

    IF temp_char BETWEEN '0' AND '9' THEN 
     SET last_number = last_number * 10 + temp_char; 
    END IF; 
    IF (temp_char_before NOT BETWEEN '0' AND '9') AND 
      (temp_char BETWEEN '0' AND '9') THEN     
     SET last_number = temp_char; 
    END IF; 

    SET position = position + 1; 
END WHILE; 

RETURN last_number; 
END// 
DELIMETER; 

luego llamar a estas funciones:

seleccione getLastNumber ("ssss111www222w"); imprimir 222

seleccionar getLastNumber ("ssss111www222www3332"); imprimir 3332

Cuestiones relacionadas