我的Web应用程序解析上传文件中的数据并将其插入数据库表中。由于输入数据(银行交易数据)的性质,重复数据可以从一个上传到另一个上传。目前我正在使用非常低效的代码来检查是否存在重复项,方法是将日期范围内的所有行从DB加载到内存中,然后迭代它们并将每个行与上传的文件数据进行比较。
毋庸置疑,随着数据集大小的增加,这会变得非常慢。
所以,我希望用SQL查询(针对MySQL数据库)替换它,以检查是否存在重复数据,例如
SELECT count(*) FROM transactions WHERE desc = ? AND dated_on = ? AND amount = ?
这很好用,但我的真实案例有点复杂。输入数据中的事务描述有时可能包含错误的标点符号(例如“BANK 12323 DESCRIPTION”通常可以表示为“BANK.12323.DESCRIPTION”),因此我们现有的(内存中)匹配逻辑对此描述执行一些清理在我们进行比较之前。
虽然这在内存中有效,但我的问题是这个清理是否可以在SQL语句中完成,因此我可以将这个匹配逻辑移到数据库中,如:
SELECT count(*) FROM transactions WHERE CLEAN_ME(desc) = ? AND dated_on = ? AND amount = ?
其中CLEAN_ME是一个删除错误数据字段的proc。
显然,最干净的(没有双关语!)解决方案是将已清理的数据存储在数据库中(在同一列中,或在单独的列中),但在我求助之前我以为我会试着找出是否有更聪明的方法。
非常感谢
答案 0 :(得分:1)
最简单的方法是在相应的列上添加唯一索引并使用ON DUPLICATE KEY UPDATE。我还建议将文件转换为csv和loading it into a temporary table以充分利用mysql的内置函数,这些函数肯定比你自己编写的任何函数都快 - 如果你认为你必须将数据拉入你自己的应用程序,而mysql做了一切。
答案 1 :(得分:1)
可以在SQL语句中完成此清理
是的,您可以在数据库层中编写stored procedure来执行此操作:
mysql> CREATE FUNCTION clean_me (s VARCHAR(255))
-> RETURNS VARCHAR(255) DETERMINISTIC
-> RETURN REPLACE(s, '.', ' ');
mysql> SELECT clean_me('BANK.12323.DESCRIPTION');
BANK 12323 DESCRIPTION
虽然这会在一张大桌子上表现得非常糟糕。
显然,最干净的(没有双关语!)解决方案是将已经清理过的数据存储在数据库中(在同一列或单独的列中),但在我诉诸于此之前我想我会尝试并找出是否有更聪明的方法。
不,就数据库而言,最干净的方式始终是最聪明的方式(只要性能不是很糟糕)。
这样做,并将索引添加到您正在进行批量比较的列中,以提高性能。如果它实际上是desc / dated-on / amount的数据类型固有的,那么在模式中通过使它成为UNIQUE索引约束来表达它。
答案 2 :(得分:0)
最干净的方法确实是确保数据库中只有正确的数据。
在此示例中,“BANK.12323.DESCRIPTION”将返回:
SELECT count(*) FROM transactions
WHERE desc LIKE 'BANK%12323%DESCRIPTION' AND dated_on = ? AND amount = ?
但是当表中包含大量数据时,这可能会导致性能问题。
答案 3 :(得分:0)
您可以采用的另一种方式如下:
插入前清理说明。
为表创建主键,该表是唯一标识条目的列的组合。听起来可能是清理描述,日期和金额。
使用'replace'或'on duplicate key'语法,这更合适。当发生现有的唯一密钥冲突时,'replace'实际上将db中的现有行替换为更新的行,例如:
REPLACE INTO交易(desc,dated_on,amount)值(?,?,?)
'on duplicate key'允许您指定在重复键错误上更新哪些列:
INSERT INTO事务(desc,dated_on,amount)值(?,?,?) ON DUPLICATE KEY SET amount = amount
通过使用多列主键,您将获得很多性能,因为主键查找通常非常快。
如果您希望保留现有的主键,还可以在这三列上创建唯一的unix。
无论您选择哪种方式,我都建议您在进入数据库之前清理说明,即使您还存储了原始描述并只使用已清理的描述进行索引。