2011-02-03 127 views
1

这是问题:在我的公司,我们有一个大型数据库,我们希望在其中执行一些自动操作。为了测试我们获得了关于6个10MB大小的csv文件的小数据样本。我们想用H2来测试我们程序的结果。 H2尽管他们最多只有1000个参赛作品,但看起来和我们以前的cvs一起工作得很好。当涉及到我们的任何10MB的文件命令未能将大型数据集加载到h2数据库中

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我能找到注册表中没有重复的日期时间列注册表中的一个正常的搜索是在整个数据集是唯一的。

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

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

也许你可以添加一些调试打印输出到你的java代码来指定导致失败的确切行。然后,将数据库(而不是源文件)转换为现有记录。也许你正在获取日期时间截断? – Randy 2011-02-03 19:27:21

回答

0

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