【发布时间】:2013-09-09 18:06:59
【问题描述】:
我正在填充两个表,它们具有一对多的关系。
所以我在outer 中插入一行,获取该行的(自动增量主键)id,然后在inner 中插入 100 行(全部带有指向outer.id 的外键)。
然后我重复 50 次。对于outer 中的每个条目,我必须插入、读取id,然后插入到内部。
这很慢。大部分时间都花在将 100 行加载到inner 中。我怀疑如果我可以在一个批处理操作中将所有 50*100 行插入inner,它会快得多。但我不知道该怎么做 - 我怎样才能让外键工作?
其他人如何提高效率?
我正在使用 Java / Spring。这 100 行插入了 JdbcTemplate.batchUpdate()。
public final void insert(final JdbcTemplate db,
final Iterable<DataBlock> data) {
String insertSql = getInsertSql();
String idQuery = getIdQuery();
ItemRowMapper.IdRowMapper mapper = new ItemRowMapper.IdRowMapper();
for (DataBlock block: data) {
Object[] outer = block.getOuter();
LOG.trace("Loading outer");
db.update(insertSql, outer);
LOG.trace("Getting index");
// currently retrieve index based on natural key, but could use last index
int id = db.query(idQuery, mapper, uniqueData(outer)).get(0);
LOG.trace("Getting inner");
List<Object[]> inner = block.getInner(id);
// most time spent here
LOG.trace(format("Loading inner (%d)", inner.size()));
innerTable.insert(db, inner);
}
}
和伪SQL:
create table outer (
integer id primary key autoincrement,
...
);
create table inner (
integer outer references outer(id),
...
);
更新 - 以下内容似乎适用于 Spring 3.1.1 和 Postgres 9.2-1003.jdbc4。
/**
* An alternative implementation that should be faster, since it inserts
* in just two batches (one for inner and one fo router).
*
* @param db A connection to the database.
* @param data The data to insert.
*/
public final void insertBatchier(final JdbcTemplate db,
final AllDataBlocks data) {
final List<Object[]> outers = data.getOuter();
List<Integer> ids = db.execute(
new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(
final Connection con) throws SQLException {
return con.prepareStatement(getInsertSql(),
Statement.RETURN_GENERATED_KEYS);
}
},
new PreparedStatementCallback<List<Integer>>() {
@Override
public List<Integer> doInPreparedStatement(final PreparedStatement ps)
throws SQLException {
for (Object[] outer: outers) {
for (int i = 0; i < outer.length; ++i) {
setParameterValue(ps, i + 1,
SqlTypeValue.TYPE_UNKNOWN, outer[i]);
}
ps.addBatch();
}
ps.executeBatch();
RowMapperResultSetExtractor<Integer> ids =
new RowMapperResultSetExtractor<Integer>(
new ItemRowMapper.IdRowMapper());
try (ResultSet keys = ps.getGeneratedKeys()) {
return ids.extractData(keys);
}
}
});
innerTable.insert(db, data.getInner(ids));
}
【问题讨论】:
-
我不明白。为什么不将
outer中的所有记录一次插入,然后将inner中的所有记录一次插入?你为什么不发布一些代码? -
如果我可以检索外部表中所有插入的索引,我可以这样做。有没有办法在一系列值上检索自动增量键?我必须手动管理密钥生成吗?可能有多个线程正在向这些表添加/删除数据。
-
对 java 代码不了解,但从数据库的角度来看,您必须以基于集合的方法执行此操作,而不是逐行执行,这听起来就像您目前正在做的那样。您可以将所有数据转储到 postgre 中的临时表中并使用临时表作为源执行插入吗?这将一次性插入所有数据,这比许多微小的插入便宜。
-
@JustBob(谢谢)忽略java,您将如何设置以便内部(或临时)表具有对外部表的外键引用?这是我的根本问题。内表的每 100 行(或多或少)引用一个不同的外行。并且 id 是自动递增的。我想我需要自己管理密钥。
-
我添加了一些 sql 表以防万一。
标签: java sql spring postgresql