使用mysql将varchar数据压缩为数字

时间:2011-03-04 12:53:53

标签: mysql regex formatting

我正在努力更新存储财务信息的系统,并且使用的表结构使用DECIMAL字段来处理相关数据。

不幸的是,我的前任,在他/她的Infinite Wisdom中将旧数据库中的字段实现为varchar。所做的数据输入验证量似乎也很轻,说得好心,并且那里有各种垃圾数据。某些字段存储NaN值,一些存储值格式化为1,234,567.89,一些存储值格式化为1.234.567.89,一些商店1234567.89,一些包括在年底货币符号,包括一些在中间的货币符号,有的甚至含有总和! (例如123 + 456)。

显然,作为DECIMAL进行投射只能帮助其中一些。如果第一个字符不是数字,我将返回0。更糟糕的是,如果数字中有逗号或多个小数点,我将得到错误的结果。

我需要某种方法将数据按摩成更有用的形式,如下:

  • 1234567.89 - > 1234567.89(简单的铸造将在这里工作)
  • 1234567.89 $ - > 1234567.89(施放这些似乎给出了正确的结果)
  • £1234567.89 - > 1234567.89(施法返回0)
  • 1,234,567,89 - > 1234567.89(此处投放返回1)
  • 1.234.567.89 - > 1234567.89(Casting给出1.234)
  • 123 + 456 - > 579.00(我不知道如何处理这些问题)
  • NaN或其他非数字数据 - > 0(没有明智的处理方法,所以只需插入0就可以了)

我自然也必须能够处理多个故障的情况,例如$ 1,234.567.89。

我想正则表达式是唯一的选择,在这里,但据我所知,MySQL只提供正则表达式匹配,它似乎并没有什么正则表达式替换功能。

如果你可以帮助我,我真的很感激。

4 个答案:

答案 0 :(得分:2)

如果您不想退出MySQL,可以始终使用control flow functionsregular expressionsreplace的组合。

SELECT 
CASE your_field 
WHEN REGEXP '^[0-9\.]*\$$' THEN DECIMAL(REPLACE(your_field,'$',''))
WHEN REGEXP...

如果您需要mysql正则表达式默认实现中不存在的功能,您可以始终使用像this one这样的UDF,它提供更高级的功能,如组捕获或替换。

顺便说一下,您是否考虑过“在MySQL外部”并使用您熟悉的编程语言来连接MySQL并以编程方式更新新字段?

答案 1 :(得分:1)

我想你可能已经想到了这一点,但是将数据拉入(比如说)CSV文件,然后写一个脚本来进行数据按摩,然后把它放回到database(使用表中的键将csv行与数据库表行匹配)?

答案 2 :(得分:0)

您可以使用REPLACE功能(例如set mycol = REPLACE(mycol,'$',''))来处理大部分内容。

对于类似1.234.567.89的内容,如果您知道自己只有两位小数,则可以使用REPLACE(mycol,'.','')然后除以100。

对于像123 + 456这样的情况,您可以使用SUBSTRPOSITION函数做些喜欢的事情 - 使用POSITION查找+,然后{ {1}}获取之前和之后的内容。 SUBSTR功能在这里也可能有用。

答案 3 :(得分:0)

我觉得没有合理的方法可以不使用脚本语言,所以我编写了以下PHP代码来解决这个问题。

function notEmptyString ($val)
{
    return ($val !== '');
}

/**
 * Make an attempt at extracting menaingful numeric data from a string that can contain all kinds of garbage
 * @param string $string
 * @return int
 */
function mungeNumber ($string)
{
    $num    = 0;
    if (($digits = preg_split ('/[^0-9]/', $string))
    && ($digits = array_filter ($digits, 'notEmptyString')))
    {
        $decimal    = (count ($digits) > 1)? array_pop ($digits): 0;
        $num        = (implode ('', $digits) . '.' . $decimal) * 1;
    }
    return ($num);
}

到目前为止,它似乎已经处理了我给出的所有测试数据,尽管我仍然想出一些更合适的病态测试案例来应对。我知道一个事实,它不会处理价值似乎是一个总和的情况,但我不认为我可以做很多事情,而且似乎是这种情况的时间是仁慈的小。

当一个字段包含两个或多个不同的数字时,结果将是一个数字,这是不幸的。但是,与集合中的其他数字相比,所讨论的数字会过大,因此应该很容易发现并手动处理。