我的问题如下:
从服务器以给定的时间间隔下载CSV文件。
必须解析文件(必须删除不必要的空格)。
必须为每个条形码更新商品价格,rrp和库存数量。
每个CSV行包含商品编号,产品标题,仓库ID,仓库名称,价格,rrp价格,库存和条形码。 items表包含大约71000行。并且条形码不是数据库中的唯一键(因为具有相同条形码的项目可以在不同的仓库中)。问题是在localhost MySQL服务器上更新需要一个多小时(大约半小时到SQLite)。有没有办法优化SQL查询以使事情更快?我目前的代码如下:
public void updateData (BufferedReader bufferedReader, Connection sqlConnection){
String csvLine = null;
PreparedStatement preparedStatement = null;
String sqlString = "UPDATE items SET price = ?, rrp = ?, stock = ? WHERE departmentid = ? AND barcode = ?";
try {
preparedStatement = sqlConnection.prepareStatement(sqlString);
while ((csvLine = bufferedReader.readLine()) != null) {
String[] splitLine = csvLine.split(";");
preparedStatement.setBigDecimal(1, new BigDecimal(splitLine[4].trim()).setScale(2, RoundingMode.CEILING));
preparedStatement.setBigDecimal(2, new BigDecimal(splitLine[5].trim()).setScale(2, RoundingMode.CEILING));
preparedStatement.setInt(3, Integer.parseInt(splitLine[6].trim()));
preparedStatement.setString(4, splitLine[2].trim());
preparedStatement.setString(5, splitLine[8].trim());
preparedStatement.executeUpdate();
}
} catch (IOException | SQLException exc) {
System.out.println(exc.getMessage());
} finally {
try {
sqlConnection.commit();
preparedStatement.close();
sqlConnection.close();
} catch (SQLException exc) {
exc.printStackTrace();
}
}
}
到目前为止,最快的解决方案看起来像@ e4c5建议使用LOAD csv数据到临时表并使用查询:
UPDATE items INNER JOIN temp_table ON items.barcode = temp_table.barcode SET items.rrp = temp_table.rrp, items.price = temp_table.price, items.stock = temp_table.stock WHERE items.barcode = temp_table.barcode AND items.departmentid = temp_table.departmentid.
有什么方法可以让它更快?
答案 0 :(得分:2)
在没有单行Java代码的情况下,导入数据的速度更快,更好。那是LOAD DATA INFILE
请注意,LOAD DATA可以为您的CSV进行一些预处理。但是在某些情况下这还不够。然后你必须得到java的帮助,但java不应该做插入。
您的java代码应该预处理CSV并生成另一个可以传递给LOAD DATA的CSV。 70000条记录轻而易举。
LOAD DATA不支持ON DUPLICATE KEY UPDATE
语法。但它确实有REPLACE语法(比重复键更新稍慢,但它仍然比java或任何其他编程语言可以提供的速度快至少一个数量级)。
如果进行实际更新是重要的事情。将DATA加载到临时表中然后转到
INSERT INTO mytable SELECT * FROM temp_table ON DUPLICATE KEY ....
答案 1 :(得分:1)
我认为在你的情况下最好的方法是使用Statement batching这里是一个例子:
sqlConnection.setAutoCommit(false);//<<------------
try {
preparedStatement = sqlConnection.prepareStatement(sqlString);
while ((csvLine = bufferedReader.readLine()) != null) {
String[] splitLine = csvLine.split(";");
preparedStatement.setBigDecimal(1, new BigDecimal(splitLine[4].trim()).setScale(2, RoundingMode.CEILING));
preparedStatement.setBigDecimal(2, new BigDecimal(splitLine[5].trim()).setScale(2, RoundingMode.CEILING));
preparedStatement.setInt(3, Integer.parseInt(splitLine[6].trim()));
preparedStatement.setString(4, splitLine[2].trim());
preparedStatement.setString(5, splitLine[8].trim());
preparedStatement.addBatch();//<<-----------add a batch
}
//execute your multiple statement as one
statement.executeBatch();//<<------------
sqlConnection.commit();//<<--------------
}
修改强>
喜欢@Mick Mnemonic在评论中提及:
如果分成较小批量的500行,你可以试试 任何差异
因此,只需一次性执行批次,您就可以小批量拆分批次,例如:
sqlConnection.setAutoCommit(false);
try {
int nbrBatch = 500;
int count = 0;
preparedStatement = sqlConnection.prepareStatement(sqlString);
while ((csvLine = bufferedReader.readLine()) != null) {
//Your code here
preparedStatement.addBatch();
if (count % nbrBatch == 0) {
statement.executeBatch();
}
count++;
}
//rest of your batch not executed
statement.executeBatch();
sqlConnection.commit();
}