使用JAVA从CSV更新MySQL

时间:2017-05-04 09:17:59

标签: java mysql performance csv jdbc

我的问题如下:

  1. 从服务器以给定的时间间隔下载CSV文件。

  2. 必须解析文件(必须删除不必要的空格)。

  3. 必须为每个条形码更新商品价格,rrp和库存数量。

  4. 每个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.有什么方法可以让它更快?

2 个答案:

答案 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();
}