任务:
给出以下HashMap结构:Map<String, Map<String, String>> mainMap = new HashMap<>()
我要将{em> inner INSERT
的每个值 COPY
或Map
放入数据库中自己的单元格中。 / p>
size()
中的mainMap
(如果为50,000)。 size()
的{{1}}是50。Map
的键。 编辑:最初,用户上载具有50列中的35列的大型电子表格。然后,我使用各种格式“清理”该数据,并为每个Map
条目将自己的15对新数据添加到innerMap中。如果不进行清理/格式化/添加,就无法直接从用户的源文件mainMap
到数据库。
一旦我完成了电子表格的迭代并构建了COPY
,那便是我需要有效地插入数据库表的时候了。
研究:
我read认为mainMap
是最初批量填充表的最佳方法,但是我对我的要求是否支持该命令感到困惑。
This post指出Postgres的查询的Prepared Statement参数限制为34464。
我假设我总共需要50 x 50,000 = 2,500,000个参数。 这相当于〜73个独立查询!
问题:
COPY
而不是所有这些参数是正确的方法吗?COPY
的值转换为HashMap
文件,将其保存在Web应用程序服务器的磁盘上,然后在我的.sql
命令中引用它,然后删除临时文件?还是可以直接将串联的COPY
传递给它,而又不会冒着SQL注入的风险?该命令经常发生,因此需要进行优化。
我找不到将Java对象转换为兼容的Postgres文本文件格式的任何示例,因此任何反馈都可以。
您将如何解决这个问题?
其他信息:
我的表已经存在,并且不能删除,因为它是我的Web应用程序的后端,并且在任何给定时间都已连接多个用户。
我知道在使用String
之前临时删除索引可以提高性能,但是我一次只需要插入或复制最多50,000行,而不需要数百万行。
StackExchange告诉我在这里问。
答案 0 :(得分:3)
虽然Java当然不是执行此类ETL的最佳选择,但使用标准INSERT
语句和预先准备好的查询确实可以并且开销很小:
conn.setAutoCommit(false);
PreparedStatement stmt = conn.prepareStatement(
"INSERT INTO my_table (col_a, col_b, ...)"
+ " VALUES (?, ?, ...)");
int batchSize = 1000;
int rows = 0;
for (Map<String, String> values : mainMap.values()) {
int i = 0;
stmt.setString(++i, values.get("col_a"));
stmt.setString(++i, values.get("col_b"));
// ...
stmt.addBatch(); // add the row to the batch
if (++rows % batchSize == 0) {
// batch-sizing: execute...
stmt.executeBatch();
}
}
if (rows % batchSize != 0) {
// a last execution if necessary...
stmt.executeBatch();
}
conn.commit(); // atomic action - if any record fails, the whole import will fail
或者,您可以将Map写出到文件中并使用CopyManager
,但是我严重怀疑这样做的速度是否比批处理插入的速度要快(不过,成百万行的记录会有所不同)。 / p>
答案 1 :(得分:2)
COPY
确实可能是初始批量上传的推荐方法,但是考虑到您的初始数据存储在Java Map
中的内存中,因此存在局限性:
STDIN
进行加载。这些选项都不对JDBC连接特别友好。COPY
。使用COPY
可能不值得进行这种处理。我会创建一个PreparedStatement
来插入您的50列,然后针对Map
中的每个mainMap.values()
(即每次50列)重复执行该准备好的语句。 / p>
您可以使用executeBatch()
提高速度。就是说,我不会一次执行全部50000,而是分批执行。
我会做这样的事情:
int BATCH_SIZE = 100;
List<String> keyNames = new ArrayList<>();
int i = 0;
try (PreparedStatement ps = conn
.prepareStatement("INSERT INTO xyz (col1, col2, ...) VALUES (?, ?, ...)")) {
for (Map<String, String> rowMap : mainMap.values()) {
int j = 1;
// You need the keynames to be in the same order as the columns
// they match.
for (String key : keyNames) {
ps.setString(j, rowMap.get(key));
j++;
}
ps.addBatch();
if (i > 0 && i % BATCH_SIZE == 0) {
ps.executeBatch();
}
i++;
}
if (i % BATCH_SIZE != 1) {
// More batches to execute since the last time it was done.
ps.executeBatch();
}
}