我正在尝试更新约。表中有3800行,分两步:
第一步是快速(<1秒)。然而,第二步需要大约3秒。也许代码中存在一些问题,因为如果我在没有第一步的情况下运行代码或在另一个表上运行第一个查询,第二步也非常快(&lt; 0.1 s)。只有在同一个表上的第一步之后执行它才会很慢。
Class.forName("net.ucanaccess.jdbc.UcanaccessDriver");
conn = DriverManager.getConnection("jdbc:ucanaccess://"
+ path +;singleconnection=true" ,"", "");
// First update (reset all values)
long start = System.currentTimeMillis();
PreparedStatement ps2 = conn.prepareStatement("UPDATE mytab SET val = 0.0;");
ps2.executeUpdate();
ps2.close();
System.out.println("Update 1: " + (System.currentTimeMillis()-start)/1000.0 + " s");
// Second update
start = System.currentTimeMillis();
conn.setAutoCommit(false);
int count = 0;
PreparedStatement ps = conn.prepareStatement("UPDATE mytab SET val = ? WHERE id = ?;");
for(int j= 1; j <= 3600; j++){
double value;
// calculate some value ....
value = 1.3;
// update table under certain conditions
if(true){
ps.setDouble(1, value);
ps.setInt(2, j);
ps.addBatch();
count++;
}
if(count > 200){
ps.executeBatch();
count = 0;
}
}
ps.executeBatch();
conn.commit();
System.out.println("Update 2: " + (System.currentTimeMillis()-start)/1000.0 + " s");
为什么第二步需要这么长时间?怎么避免这个?
(我问同样的问题here已经但没有成功)。
修改: 如果我使用旧的JDBC-ODBC桥(和Java 7)尝试两个查询,则在第一个查询之后执行时,第二个查询非常快(<1秒)。所以我认为这肯定是一些UCanAccess问题。
答案 0 :(得分:2)
原因是你有一个来自mysql的EXTERNAL循环。调用程序一直调用相同的预处理语句,但每次调用/执行都有很多开销,即使实际语句很小。
理想的解决方案是将条件移动到SQL(如果可行)并让mysql处理它。在这种情况下,它通常非常快:
UPDATE mytab SET value=1.3 WHERE <condition>;
实际上,您可以将两者结合使用IF语句(同样,如果mysql具有该条件的正确数据:
UPDATE mytab SET value=IF(<condition>, 1.3, 0.0)
---编辑--- 如果事务处于活动状态,则第一个查询会将所有记录放在一边,每个后续查询都必须修改现有(实时事务)。您将有3800个交易冲突。尝试在两个查询之间放置一个COMMIT
答案 1 :(得分:1)
根据对问题的评论确定,原始测试是欺骗性的,因为当第一个查询将值清零时,它强制第二个查询更新每一行。但是,当省略第一个查询时,第二个查询只是使用现有测试值“更新”行。由于行实际上没有被更改,因此它们没有刷新到数据库文件,因此第二个查询运行得更快。对第二个查询强制行更新的测试值进行调整,无论是否执行了第一个查询,都会产生相同的性能。
至于更新的性能,而不是直接更新每一行,你可能会发现它更快
我刚试过这个,它似乎在大约四分之一的时间内逐行直接更新主表:
library(dplyr)
df %>% mutate(LKUP = ifelse(LKUP > 10, LKUP/2, LKUP))
# ID LKUP
# 1 A 2.0
# 2 A 4.0
# 3 A 10.0
# 4 A 12.5
# 5 B 8.5
# 6 B 9.0
# 7 B 6.0
# 8 B 3.0