【问题标题】:Store array by array in SQLite-Database在 SQLite 数据库中按数组存储数组
【发布时间】:2014-01-12 16:05:10
【问题描述】:

在我的 Java 应用程序中,我需要在硬盘上存储一个大表,因为我希望它是持久的。

我的第一次尝试是这样的: (i & j 可以上升到 300.000 甚至更多,所以我有一个包含 300.000^2 个双条目的数组,这会导致我的系统崩溃。)

stmt.executeUpdate("DROP TABLE IF EXISTS calculations;");
stmt.executeUpdate("CREATE TABLE calculations (factorA, factorB, result);");
double temp = 0;
for (i = 0; i < datasource.size(); i++) {
    for (int j = 0; j < datasource.size(); j++) {
        if (i != j) {
            temp = calc(datasource.get(i),datasource.get(j));
            stmt.execute("INSERT INTO calculations (factorA, factorB, result) VALUES ('"+i+"','"+j+"','"+temp+"')");
        }
    }
}

现在,这执行得非常慢,可能是因为 SQL 命令是一个字符串等。

我的新猜测是,最好先计算 10.000 个 i 的结果,然后将它们作为一个单元存储到数据库中。

但在我尝试实施之前,有人有更好的主意吗?数据库使用不是强制性的,只是一个简单的访问和快速实施。

【问题讨论】:

  • 确保将批量插入作为事务进行; sqlite.org/lang_transaction.html - 这将使它们在 SQLite 引擎中排队,然后当您指示事务完成时,将快速提交它们,而不是一一进行。
  • 您需要快速写入还是只需要快速读取?你要提取多少行?因子A+因子B的组合可以用作索引吗?
  • 其实我两者都需要,快速的写作和阅读,但在第一次写作。
  • 好的,我现在使用“PreparedStatement”,看起来效果很好。

标签: java arrays database performance sqlite


【解决方案1】:

尝试在一个Transaction 中添加每个n 左右的行(假设失败不是问题,例如如果某些行无法插入,您可以继续而不回滚之前的行)。 在循环之外声明一个计数器:

int n = 1000; //commit every 1000 rows, or you can tweak
int count = 0; //counter for rows inserted

在外循环中开始事务。增加并检查内循环中的计数器

if(count % n == 0){
  //commit the transaction
}
count++

(别忘了在外循环中重新打开Transaction)

http://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html

【讨论】:

  • 谢谢,这就是我现在正在做的事情。但是,阅读仍然很慢。我只是认为,当您需要访问非常大的表时,SQLite 不是要走的路。但还有什么?!
  • 你的查询慢吗,直接在Sql IDE中运行试试?尝试分页,每隔几行读/写一次,这样可以节省内存。
【解决方案2】:
        int BLOCK_SIZE = 15000;
        stmt.executeUpdate("DROP TABLE IF EXISTS calculations;");
        stmt.executeUpdate("CREATE TABLE calculations (factor_idx text NOT NULL PRIMARY KEY,result text NOT NULL);");
        double temp = 0;
        int block_ctr = 1;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < datasource.size; i++) {
            for (int j = 0; j < datasource.size; j++) {
                if (i != j) {
                    temp = calc(a, b);
                    // init the statement when counter = 1
                    if (block_ctr == 1) {
                        sb.append("INSERT INTO 'calculations' SELECT '" + i + "_" + j + "' AS 'factor_idx', '" + temp + "' AS 'result'");
                    }


                    // then commit only every BLOCK_SIZE blocks
                    if (block_ctr <= BLOCK_SIZE) {
                        sb.append("UNION SELECT '" + i + "_" + j + "','" + temp + "'");
                    } else {
                        stmt.execute(sb.toString());
                        sb.setLength(0); // better then creating a new sb 
                        block_ctr = 0;
                    }
                    block_ctr++;
                }
            }
        }

我减少了列数,并使用 StringBuilder 创建了一个组合语句。它应该更快,允许使用您创建的连接 i 和 j 的主键列上的索引进行更快的读取。试着告诉我,我很好奇:)

【讨论】:

  • 很有趣,当我得到 >1 时,这就会停止……嗯,我需要弄清楚为什么会这样。
  • 当我将 BLOCK_SIZE 减少到 500 时,我得到这个:[SQLITE_ERROR] SQL 错误或缺少数据库(复合 SELECT 中的术语太多)。当我将 100 用于 BLOCK_SIZE 时,它的执行速度更快,但不如preparedstatement 快:-D
  • 我不知道复合 SELECT 中术语的限制......我想我学到了一些东西 :) 我认为我在单个索引列上更快读取的想法比选择更有效i 和 j。将preparedStatement 与我建议的表结构一起使用。
  • 使用 8000x8000 进行 i 和 j 阅读效果非常好!这是我要走的路……非常感谢!
猜你喜欢
  • 2013-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-07
  • 2021-04-21
  • 2011-11-13
  • 1970-01-01
相关资源
最近更新 更多