如何在同一个sql查询中更新和选择记录

Posted

技术标签:

【中文标题】如何在同一个sql查询中更新和选择记录【英文标题】:How to update and select records in the same sql query 【发布时间】:2018-04-09 14:20:29 【问题描述】:

作为任何产品环境中的典型场景,我们有多个节点从数据库 (oracle) 中获取和处理项目。

我们要确保每个节点都从数据库中获取唯一的一组项目并对其进行操作。为了使这成为可能,我们正在查看是否可以更新记录状态(例如,空闲到进行中),并且相同的更新查询返回它更新的记录。这样,每个节点都会根据自己的记录集进行操作,而不会干扰彼此的记录集。

出于维护原因,我们希望避免使用 pl/sql。我们尝试使用“选择更新”,但在少数情况下,它会导致数据库锁定更长的时间。

关于如何通过简单的 sql 或 hibernate 实现这一点的任何建议(因为我们也有可用的 hibernate 选项)?

【问题讨论】:

这对我来说看起来不像是一个编码问题,所以它可能会被搁置,但感觉即使你使用一些状态标志,这也不能用一些简单的 sql 语句来处理。您需要在应用层有逻辑来处理丢失的连接、未处理的行等,以便正确重置或更新状态标志。 @JoakimDanielson 在生产环境中运行多个节点以进行负载平衡。节点可以使用 status 列,以便在处理任何项目之前,节点可以先声明它,这样其他节点就不会拿起相同的项目并开始处理它,这可能会导致多个处理相同的项目。 【参考方案1】:

对此有一些想法。首先,在 Oracle 中,您可以使用 RETURNING 子句作为更新语句的一部分,将正在更新的表中的选择列(例如主键)返回到集合中。但是这种方法确实需要 PL/SQL 才能工作,因为您需要使用集合,尽管 BULK 事务会减轻使用 PL/SQL 的一些缺点。

另一种选择是在表中添加一个列,您可以在其中指示哪个节点正在处理记录,类似于您对指示空闲或处理状态的状态列的想法。如果未处理,则此值为 NULL,或者是唯一标识处理记录的节点或进程的值。

一点额外的research 导致this post 在 Stack 上关于在 Java 中使用 Oracles RETURNING INTO 语句。它还直接回到 Oracle 自己的 documentation,主题是 Java 支持的 Oracle DML Returning feature

【讨论】:

@Santinel 在我们的一个模块中,我们有 Node 列,但我们希望摆脱这种设计,因为在不久的将来我们的目标是云。在云中,我们将根据服务器上的负载动态生成/删除节点。我们还想避免使用 pl/sql,因为我们的项目中可用的专业知识很少。即使我们现在写pl/sql来解决当前的场景,但是未来为了改变业务逻辑还是会出现维护问题。我们希望将业务登录保留在 java/app 层而不是 pl/sql。 见上面的编辑。使用我对 RETURNING INTO 子句的第一个建议,看起来你可以在没有 PL/SQL 的情况下在 Java 中做到这一点。 我们试过这个,但如果只返回一行,这个效果很好。如果有多行则抛出错误....java.sql.SQLException: ORA-01422: exact fetch returns more than requested number of rows 要返回多行,您可能需要使用RETURNING col, list BULK COLLECT INTO ... 语法。关键字是 BULK COLLECT 通知 oracle 预计会有多个结果记录。【参考方案2】:

最终我们找到了解决问题的方法。我们的问题陈述:按创建时间从列表顺序中获取前 100 个项目。所以我们想要应用的逻辑是基于FIFO。因此,在这种情况下,每个节点将从数据库中提取前 100 个项目并开始对其进行处理。这样,每个节点将处理自己的一组项目,而不会在彼此的路径上重叠。

我们通过在 oracle 数据库中创建 TYPE 来实现这一点,然后使用 hibernate 声明这些项目并将声明的项目临时存储在 TYPE 中。代码如下:

create type TMP_TYPE as table of VARCHAR2(1000);

//休眠代码

 String query = "BEGIN UPDATE OUR_TABLE SET OUR_TABLE_STATUS = 'IP' WHERE OUR_TABLE_STATUS = 'ID' AND ID_OUR_TABLE IN (SELECT ID_OUR_TABLE FROM (SELECT ID_OUR_TABLE FROM OUR_TABLE ORDER BY AGEING_SINCE ASC ) ) AND ROWNUM < 101 RETURNING UUID BULK COLLECT INTO ?;END;";

 Connection connection = getSession().connection();
 CallableStatement cStmt = connection.prepareCall(query);

 cStmt = connection.prepareCall(query);
 cStmt.registerOutParameter(1, Types.ARRAY, " TMP_TYPE ");
 cStmt.execute();
 String[] updateBulkCollectArr = (String[]) (cStmt.getArray(1).getArray());

`

从这里得到想法Oracle Type and Bulk Collect 谢谢@Sentinel

【讨论】:

以上是关于如何在同一个sql查询中更新和选择记录的主要内容,如果未能解决你的问题,请参考以下文章

Laravel框架中如何使用事件记录SQL查询到日志

如何查询历史sql

如何通过将一个表中的 id 与另一个表匹配来选择和更新一个表中的记录?

如何在pl sql中跟踪异常终止

SQL:如何从重复行中选择第一条记录?

如何在选择查询中增加 sql 计数器