无法将大型数据集加载到h2数据库中

时间:2011-02-03 19:20:08

标签: java sql h2

问题在于:在我的公司,我们有一个大型数据库,我们希望在其中执行一些自动化操作。为了测试我们得到了关于6个10MB大小的csv文件的小数据样本。我们想用H2来测试我们程序的结果。 H2似乎与我们以前的cvs一起工作得很好,尽管它们最多只有1000个条目。当涉及我们的任何10MB文件命令

insert into myschema.mytable (select * from csvread('mycsvfile.csv'));

报告失败,因为其中一个注册表被认为是重复的,并且冒犯了我们的主键约束。

Unique index or primary key violation: "PRIMARY_KEY_6 ON MYSCHEMA.MYTABLE(DATETIME, LARGENUMBER, KIND)"; SQL statement:
insert into myschema.mytable (select * from csvread('src/test/resources/h2/data/mycsvfile.csv')) [23001-148] 23001/23001

将mycsvfile.csv分解成更小的部分我能够看到问题在插入大约10000行后开始出现(尽管数量因我使用的数据而异)。但是,如果我将文件分成碎片然后单独运行命令,我可以插入超过10000行。但即使我设法手动插入所有数据,我也需要一个自动方法来填充数据库。

由于运行命令不会给我导致问题的行,我猜测问题可能是csvread例程中的一些缓存。

然后我创建了一个小型java程序,可以手动将数据插入H2数据库。无论我是否批处理命令,关闭并打开连接1000行h2报告我试图复制数据库中的条目。

org.h2.jdbc.JdbcSQLException: Unique index or primary key violation: "PRIMARY_KEY_6 ON MYSCHEMA.MYTABLE(DATETIME, LARGENUMBER, KIND)"; SQL statement:
INSERT INTO myschema.mytable VALUES ( '1997-10-06 01:00:00.0',25485116,1.600,0,18 )  [23001-148]

使用emacs对该注册表进行常规搜索我可以发现注册表没有重复,因为datetime列在整个数据集中是唯一的。

由于公司销售该信息,我无法为您提供测试数据。但这是我的表定义如何。

create table myschema.mytable (
   datetime timestamp,
   largenumber numeric(8,0) references myschema.largenumber(largecode),
   value numeric(8,3) not null,
   flag numeric(1,0) references myschema.flag(flagcode),
   kind smallint references myschema.kind(kindcode),
   primary key (datetime, largenumber, kind)
);

这就是我们的csv的样子:

datetime,largenumber,value,flag,kind
1997-06-11 16:45:00.0,25485116,0.710,0,18
1997-06-11 17:00:00.0,25485116,0.000,0,18
1997-06-11 17:15:00.0,25485116,0.000,0,18
1997-06-11 17:30:00.0,25485116,0.000,0,18

将填充我们的测试数据库的java代码(原谅我丑陋的代码,我绝望了:)

private static void insertFile(MyFile file) throws SQLException {
    int updateCount = 0;
    ResultSet rs = Csv.getInstance().read(file.toString(), null, null);
    ResultSetMetaData meta = rs.getMetaData();
    Connection conn = DriverManager.getConnection(
            "jdbc:h2:tcp://localhost/mytestdatabase", "sa", "pass");
    rs.next();
    while (rs.next()) {
        Statement stmt = conn.createStatement();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < meta.getColumnCount(); i++) {
            if (i == 0)
                sb.append("'" + rs.getString(i + 1) + "'");
            else
                sb.append(rs.getString(i + 1));
            sb.append(',');
        }
        updateCount++;
        if (sb.length() > 0)
            sb.deleteCharAt(sb.length() - 1);

        stmt.execute(String.format(
                "INSERT INTO myschema.mydatabase VALUES ( %s ) ",
                sb.toString()));
        if (updateCount == 1000) {
            conn.close();
            conn = DriverManager.getConnection(
                    "jdbc:h2:tcp://localhost/mytestdatabase", "sa", "pass");
            updateCount = 0;
        }
    }
    if (!conn.isClosed()) {
        conn.close();
    }
    rs.close();
}

如果有要求,我很乐意提供更多信息。

修改

@Randy我总是在运行命令之前检查数据库是否干净,在我的java程序中,我有一个例程来删除无法插入的文件中的所有数据。

 select * from myschema.mytable where largenumber  = 25485116;
 DATETIME   LARGENUMBER     VALUE   FLAG    KIND  
 (no rows, 8 ms)

1 个答案:

答案 0 :(得分:0)

我唯一能想到的是桌面上有一个触发器,它将时间戳设置为“now”。虽然这不能解释为什么你成功使用几行,但它可以解释为什么主键被违反。