在 Oracle 中选择前 N 个带有“更新跳过锁定”的选项

问题描述 投票:0回答:2

在 Oracle 中,我可以使用

选择排序表中的前 1 条消息
select messageid from(
    select 
        messageid, 
        RANK() over (order by messageid asc) as msg_rank 
    from messages
) where msg_rank=1;

正如我在上一个问题中发现的那样,我可以仅使用

选择一行
select * from messages where rownum < 2 for update skip locked;

但是我无法将这两个概念合并在一起

select messageid from(
    select 
        messageid, 
        RANK() over (order by messageid asc) as msg_rank 
    from messages
) where msg_rank=1 for update skip locked;

-- results in error
-- ORA-02014: cannot select FOR UPDATE from view with DISTINCT, GROUP BY, etc.

如何通过 readpast 锁定选择前 N 个?

sql oracle locking top-n
2个回答
3
投票

这行得通吗?

select messageid from messages 
    where messageid in (
       select messageid from(
         select 
          messageid, 
          RANK() over (order by messageid asc) as msg_rank 
          from messages
       ) where msg_rank=1
    )
  for update skip locked;

0
投票

从事类似的任务,这是我的发现

SELECT BANNER_FULL FROM v$version
-- Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
-- Version 19.8.0.0.0

方法一:光标功能

SQL

CREATE OR REPLACE FUNCTION lockRows RETURN SYS_REFCURSOR IS
    cur SYS_REFCURSOR;
BEGIN
    OPEN cur FOR select /*+ FIRST_ROWS(10) */ TASK_ID from QUEUE where <...> FOR UPDATE SKIP LOCKED;
    return cur;
END;

Java

CallableStatement cs = connection.prepareCall("{ ? = call lockRows }")
cs.setFetchSize(topN);
cs.registerOutParameter(1, OracleTypes.CURSOR);
cs.execute();
ResultSet resultSet = (ResultSet) cs.getObject(1);
while(topN-- > 0 && resultSet.next()) {
  String taskId = resultSet.getString(TASK_ID);
  // update the row (where clause will not match it anymore)
}

删除

FIRST_ROWS
优化器提示和
setFetchSize
时没有注意到太大变化。

方法 2:准备好的声明

Java

PreparedStatement ps = connection.prepareStatement(
"select /*+ FIRST_ROWS(10) */ TASK_ID from QUEUE where <...> FOR UPDATE SKIP LOCKED")
ps.setFetchSize(topN);
ps.execute();

ResultSet resultSet = ps.getResultSet();
while(topN-- > 0 && resultSet.next()) {
  String taskId = resultSet.getString(TASK_ID);
  // update the row (where clause will not match it anymore)
}

测试

  1. 在 QUEUE 表中创建 20,000 行
  2. 启动多个工作线程(> 10),一次连续处理 1-40 条记录,直到处理完所有 20,000 条记录。

记录(记录)返回行数小于 topN 的所有出现情况。 这些事件只有在最后才会被注意到 - 一些工作人员获得的行数少于他们要求的行数。
如果准备好的语句确实会锁定表中的所有行,则会发生更多事件。

行号

如果在PreparedStatement中使用ROWNUM -

where <...> and ROWNUM < topN
- 在很多事件中,工作人员获得的行数少于他们要求的行数。

结论

在线阅读建议只有游标方法才能按预期执行,我编写了

PreparedStatement
实现只是为了验证它不起作用 - 1 个工作人员会锁定所有行,导致并发性较差。
鉴于我的发现,我得出结论,不需要光标,
PreparedStatement
也可以正常工作。

© www.soinside.com 2019 - 2024. All rights reserved.