【问题标题】:Deleting records from Oracle 11g with JDBC使用 JDBC 从 Oracle 11g 中删除记录
【发布时间】:2016-02-05 07:27:10
【问题描述】:

我需要从包含 900 万条记录的表中删除 10000 条记录。要删除的 ID 将从复杂查询中获取并存储在 Java 集合中。

我有 3 种方法来实现这个

1) 创建一个prepared statement,将10000个语句加入batch并执行。

语句看起来像这样

Delete from <table_name> where id=?;

2) 编写一个“in”查询而不是批量使用“=”。 喜欢

在此之下,10 000 个 ID 可以在 Java 代码中创建为逗号分隔值并添加到查询中。或者,将 10000 个 ID 插入到临时表中,并在子查询中从该表中进行选择。

Delete from <table_name> where id in (<CSV>);
                 or
Delete from <table_name> where id in (select id from <temp_table>);

表中没有约束和索引。而且我不能添加一个,因为我正在处理现有的表。

第一个选项需要很长时间才能完成。它运行了 15 小时,但仍未完成。

【问题讨论】:

  • 您是说该表在id 列上甚至没有主键?
  • 这听起来很奇怪,但是是的! id 上没有主键 :)
  • 具有id 但没有主键的表。这只是可怕的数据库设计。任何查询都无法在这样的表上执行,那有什么意义呢?
  • 这让我的工作更加努力。但是这些表来自我们不维护的不同数据源。我不能说我没有答案,所以请大家帮忙。
  • 如果您可以添加新的数据库对象(但不能更改现有对象),您可以考虑为您的表添加物化视图。与普通视图不同,物化视图甚至可以在基础表中没有索引的列上具有索引。然后,您可以直接删除此视图中的记录,并有一个额外的触发器来删除基础表中的记录。只是一个想法(没试过)

标签: java sql oracle jdbc


【解决方案1】:

您的第一个版本有 1000 个值的限制,并且往往表现不佳。第二种方法可能性能更好,但您必须有一个全局临时表并填充它是一个额外的步骤。

您可以将 Java 集合转换为 Oracle 集合。您可以为此创建自己的表类型,但是您可以在此处使用诸如 ODCINUMBERLIST 之类的内置表类型。您可以将其视为表集合表达式。

根据您的 Java 集合类型,详细信息可能略有不同,但大纲如下:

ArrayDescriptor aDesc = ArrayDescriptor.createDescriptor("SYS.ODCINUMBERLIST",
  conn);
oracle.sql.ARRAY oraIDs = new oracle.sql.ARRAY(aDesc, conn, yourJavaCollectinOfIDs);

cStmt = (OracleCallableStatement) conn.prepareCall(
  "Delete from <table_name> 
   where id in (select column_value from table(?))");
cStmt.setArray(1, oraIDs);
cStmt.execute();

除非它已经是一个简单的数组,否则您需要在调用中将您的 Java 集合转换为数组;例如如果你使用的是名为 yourArrayList 的 ArrayList,你会这样做:

oracle.sql.ARRAY oraIDs = new oracle.sql.ARRAY(aDesc, conn, yourArrayList.toArray());

您仍然会因缺少主键或索引而受苦,但与 CSV 列表(或多个 CSV 列表或在一起,因为您有超过 1000 个 ID)相比,它会给 Oracle 提供更好的优化机会。

【讨论】:

  • 感谢亚历克斯的回复。尝试使用内置类型实现您的方法,但以“java.sql.SQLException:无法转换为内部表示”结束。使用了“ODCINUMBERLIST”和“ODCIVARCHAR2LIST”等类型。有什么帮助吗?我的 ID 不是数字,它们是字符串。它们看起来像 1:123
  • @ShivaMothkuri - 在 createDescriptor 调用中,您使用的是 ODCIVARCHAR2LIST 还是 SYS.ODCIVARCHAR2LIST?您的 Java 集合是如何定义的?
  • 使用 SYS.ODCIVARCHAR2LIST。我的收藏是 ArrayList。字符串中的冒号(:)会影响什么吗?
  • @ShivaMothkuri - 我认为您只是缺少一个 toArray() 调用来将您的 ArrayList 转换为数组。我已将其添加到答案中。
  • 哦,是的!你是对的。我错过了 toArray() 电话。它现在正在工作。谢谢。通过这种方法,删除以 10000 条记录/3 秒完成。一件事要从你那里确认。我是否需要手动删除这些 Oracle 阵列,否则它们会被自动删除?
【解决方案2】:

您不应该通过从您的 java 代码中执行 10000 条语句来使用第一个选项。

创建临时表是个好主意。但大多数时候你不能有一个包含超过 1000 个项目的 IN (...) 子句。因此,您使用CSV 的方法可能不会成功。

你可以去

Delete from <table_name> where id in (select id from <temp_table>);

但是这种方式也没有优化。最好将您的 delete 语句更改为:

Delete from <table_name> m where exists (select id from <temp_table> t where m.id = t.id);

但是,如果您经常执行此类操作,强烈建议您为您的&lt;table_name&gt; 甚至您的&lt;temp_table&gt; 添加一些约束和索引。它将像魅力一样提高您的操作执行时间。

【讨论】:

  • Oracle 为limit is always 1000,与项目大小无关。
  • @AlexPoole:感谢您的提示。没错,我会编辑我的答案。
  • 如果IN 版本与EXISTS 版本的性能不同,我会感到非常惊讶。
【解决方案3】:

WHERE ... IN (...) 是要走的路。

IN 子句可以引用您已填充的临时表(您最初的想法),或者它可以包含任何选择(固定)数量的 ?参数。它将减少 db 往返次数,系数等于所选数量,但不一定是 1。迭代您的集合并分块处理它。

【讨论】:

  • 所以,您的意思是在准备好的语句中添加 10,000 个问号并使用 java 循环preparedstatement.setString(1, value),preparedstatement.setString(2, value)?
  • 不,我的意思是任何选择的问号数量。比如说,50. 或 100. 或 200。显然,其他答案表明,由于施加了限制,10000 是不可能的。
【解决方案4】:

像这样试试。

Delete from <table_name> where
    id in (1, 2, 3, ... ,1000)
    or id in (1001, 1002, ... , 2000)
    ....

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-09
    • 2016-10-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-17
    • 2019-11-25
    • 2013-11-02
    相关资源
    最近更新 更多