我正在努力更新存储财务信息的系统,并且使用的表结构使用DECIMAL字段来处理相关数据。
不幸的是,我的前任,在他/她的Infinite Wisdom中将旧数据库中的字段实现为varchar。所做的数据输入验证量似乎也很轻,说得好心,并且那里有各种垃圾数据。某些字段存储NaN值,一些存储值格式化为1,234,567.89,一些存储值格式化为1.234.567.89,一些商店1234567.89,一些包括在年底货币符号,包括一些在中间的货币符号,有的甚至含有总和! (例如123 + 456)。
显然,作为DECIMAL进行投射只能帮助其中一些。如果第一个字符不是数字,我将返回0。更糟糕的是,如果数字中有逗号或多个小数点,我将得到错误的结果。
我需要某种方法将数据按摩成更有用的形式,如下:
我自然也必须能够处理多个故障的情况,例如$ 1,234.567.89。
我想正则表达式是唯一的选择,在这里,但据我所知,MySQL只提供正则表达式匹配,它似乎并没有什么正则表达式替换功能。
如果你可以帮助我,我真的很感激。
答案 0 :(得分:2)
如果您不想退出MySQL,可以始终使用control flow functions和regular expressions或replace的组合。
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
这样的情况,您可以使用SUBSTR
和POSITION
函数做些喜欢的事情 - 使用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);
}
到目前为止,它似乎已经处理了我给出的所有测试数据,尽管我仍然想出一些更合适的病态测试案例来应对。我知道一个事实,它不会处理价值似乎是一个总和的情况,但我不认为我可以做很多事情,而且似乎是这种情况的时间是仁慈的小。
当一个字段包含两个或多个不同的数字时,结果将是一个数字,这是不幸的。但是,与集合中的其他数字相比,所讨论的数字会过大,因此应该很容易发现并手动处理。