How do you extract a numerical value from a string in a MySQL query?

I have a table with two columns: price (int) and price_display (varchar).

price is the actual numerical price, e.g. "9990"

price_display is the visual representation, e.g. "$9.99" or "9.99Fr"

I've been able to confirm the two columns match via regexp:

price_display not regexp format(price/1000, 2)

But in the case of a mismatch, I want to extract the value from the price_display column and set it into the price column, all within the context of an update statement. I've not been able to figure out how.

Thanks.


Solution 1:

This function does the job of only returning the digits 0-9 from the string, which does the job nicely to solve your issue, regardless of what prefixes or postfixes you have.

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

Copied here for reference:

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

Solution 2:

One approach would be to use REPLACE() function:

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

This works for the examples data you gave:

'$9.99'
'9.99Fr'

Both result in 999 in my test. With an update like this, it's important to be sure to back up the database first, and be cognizant of the formats of the items. You can see all the "baddies" by doing this query:

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

Solution 3:

For me CASTING the field did the trick:

CAST( price AS UNSIGNED ) // For positive integer

CAST( price AS SIGNED ) // For negative and positive integer

IF(CAST(price AS UNSIGNED)=0,REVERSE(CAST(REVERSE(price) AS UNSIGNED)),CAST(price AS UNSIGNED)) // Fix when price starts with something else then a digit

For more details see:

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

Solution 4:

This is a "coding horror", relational database schemas should NOT be written like this!

Your having to write complex and unnecessary code to validate the data.

Try something like this:

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

In addition, you can use a float, double or real instead of a integer.

If you need to store currency data, you might consider adding a currency field or use the systems locale functions to display it in the correct format.