我正在尝试向数据库插入一些单词,如果该单词已存在于数据库中,则返回新插入的id或现有id。
我发现我可以使用PreparedStatement
并使用Statement.RETURN_GENERATED_KEYS
执行此操作。但是PreparedStatement
非常缓慢。我需要一次插入5000个单词。我可以通过在for循环中运行单个查询来实现它的另一种方法:
public ArrayList<Integer> addWords(ArrayList<String[]> allTermsForTag) {
ArrayList ids = new ArrayList<Integer>();
ResultSet rs = null;
try{
Statement st = connection.createStatement();
for (String[] articleTerms: allTermsForTag) {
for(String term: articleTerms) {
String query = "WITH a AS (INSERT INTO tag (name) SELECT '"+term+"' WHERE NOT EXISTS (SELECT name FROM tag WHERE name = '"+term+"') " +
"RETURNING id) SELECT id FROM a UNION SELECT id FROM tag WHERE name = '"+term+"'";
rs = st.executeQuery(query);
while (rs.next())
{
int id = rs.getInt(1);
ids.add(id);
System.out.printf("id: "+id);
}
}
}
rs.close();
st.close();
}catch(SQLException e){
System.out.println("SQL exception was raised while performing SELECT: "+e);
}
return ids;
}
这很适合我需要的东西,但这也太慢了。
我编写的另一种方法使用executeBatch()
,但它不会返回ids:
public ArrayList<Integer> addWords(ArrayList<String[]> allTermsForTag){
ResultSet rs = null;
ArrayList ids = new ArrayList<Integer>();
try{
Statement st = connection.createStatement();
for (String[] articleTerms: allTermsForTag) {
for(String term: articleTerms) {
String query = "WITH a AS (INSERT INTO tag (name) SELECT '"+term+"' WHERE NOT EXISTS (SELECT name FROM tag WHERE name = '"+term+"') " +
"RETURNING id) SELECT id FROM a UNION SELECT id FROM tag WHERE name = '"+term+"'";
st.addBatch(query);
}
st.executeBatch();
rs = st.getGeneratedKeys();
while (rs.next()) {
int id = rs.getInt(1);
ids.add(id);
}
}
st.close();
return ids;
}catch (SQLException e){
System.out.println("SQL exception was raised while performing batch INSERT: "+e.getNextException());
System.out.println("dub");
}
return null;
}
所以问题是 - 如何在使用executeBatch()
时获取ID,或者如果这不可能,如何解决这个问题?我需要它尽可能快地工作,因为会有大量INSERT操作和大量数据。
谢谢!
答案 0 :(得分:1)
executeBatch
可以在最新的PgJDBC版本中返回生成的密钥。请参阅issue 195和pull 204。您必须使用the prepareStatement
variant that takes a String[]
of returned column names。
然而......退后一步。解决方案不是循环。解决方案几乎从不循环。
在这种情况下,您几乎肯定会COPY
通过the PgJDBC CopyManager
API将COPY
数据用于TEMPORARY
表格。然后执行INSERT INTO ... SELECT ... RETURNING ...
将临时表的内容插入到最终表中并返回任何生成的字段。您还可以执行SELECT
加入临时表以返回任何已存在的表。这基本上是bulk upsert或密切相关的批量插入 - 如果不存在。
如果由于某种原因你无法做到这一点,那么下一个最佳选项可能是具有大INSERT
列表的多值VALUES
,但这需要一些丑陋的动态SQL。由于如果行已经存在,您需要现有值,您可能也需要可写的CTE。实际上,只需使用COPY
和查询来进行表合并。
答案 1 :(得分:0)
Set set = new HashSet();
try {
PreparedStatement ps = cn.prepareStatement("delete from myTable where... ",
Statement.RETURN_GENERATED_KEYS);
ps.setInt(1,200);
ps.setInt(2,262);
ps.setString(3, "108gf99");
ps.addBatch();
ps.setInt(1,200);
ps.setInt(2,250);
ps.setString(3, "hgfha");
ps.addBatch();
ps.executeBatch();
ResultSet rs = ps.getGeneratedKeys();
while (rs.next()){
set.addAll(Collections.singleton(rs.getLong(1)));
}
System.out.println(set);
} catch (SQLException e) {
e.printStackTrace();
}