如何为 oracle 中的每个线程选择和阻止行?(PostgreSQL 有一个工作示例)

Posted

技术标签:

【中文标题】如何为 oracle 中的每个线程选择和阻止行?(PostgreSQL 有一个工作示例)【英文标题】:How can I select and block row for each thread in oracle?(There is a working example for PostgreSQL) 【发布时间】:2019-01-10 09:19:30 【问题描述】:

我有桌子products:

id  name count description          status  expiration_date
1   Hz    1    Test1537208034036    NEW     2018-09-17
2   Pz    3    Test1537209516789    NEW     2018-10-17
3   Uz    7    Test1537210999618    NEW     2018-08-17
4   Mz    12   Test1537212483215    NEW     2018-11-17

我需要选择 count 且状态 = NEW 最大的行。为此,我可以在 postgreSQL 上写:

Select * 
from products 
where status = 'NEW' 
order by count desc 
LIMIT 1

但如果 4 个线程将开始执行此选择 - 每个线程将获得相等的行(计数 = 12)。我可以重写这个查询,它工作正常:

Select * 
from products 
where status = 'NEW' 
order by count desc 
LIMIT 1 
for update of products skip locked 

但我不能在 Oracle 中重复这一点。

SELECT p.* 
from (
  Select * 
  from products 
  where status = 'NEW' 
  order by count desc
) p 
WHERE p.ROWNUM = 1 
FOR UPDATE OF products SKIP LOCKED

Oracle 没有 LIMIT 1rownum 的工作方式不同。我需要从 ordered 表中获取第一行(order by descbut this row not locked

我怎样才能像 PostgreSQL 一样重复逻辑。也许我的选择是错误的。

如果你看起来更轻松,这就是我想要的 - 我有桌子和很多线程。我需要每个线程从数据库中接收最旧的行(或最大的计数),并且只有它是一个。其他线程不应该收到它。下一个线程应该接收它之后最旧(或最大)的行。

【问题讨论】:

Oracle 有 fetch first x rows only 而不是 limit(实际上 Postgres 也支持这种语法)。但是您需要使用别名获取子选择的 rownum inside 并在 outside 上使用它。您当前的代码使用 outer 查询中的rownum,而不是内部查询。 @a_horse_with_no_name Oracle 的一个例子并不准确。只是问题是 oracle 使用 2 选择而不是锁定行。如果更准确地说,那么 4 个线程阻塞同一行 如果我使用别名获取子选择内部的 rownum 并在外部使用它 - 所有线程都会立即执行第一个选择而不会阻塞。相反,如果我将按单行排序,因为内部选择会给我一行。我必须对所有行进行排序并选择一个然后阻止它 【参考方案1】:

我建议使用动态 sql。锁是在获取时获取的。您获取一条记录,它同时被锁定。请记住,一旦您完成事务 - 提交或回滚,记录就会解锁。

这是一个简短的函数示例,它为您的线程返回下一个products.id

create or replace function get_next_unlocked_id
return number
is
  cRefCursor sys_refcursor;
  rRecord    products%rowtype;
begin
  -- open cursor. No locks so far
  open cRefCursor for 
    'select * from products '||
    'where status = ''NEW'' '||
    'order by count desc '||
    'for update skip locked';

  -- we fetch and lock at the same time one record
  fetch cRefCursor into rRecord;

  -- close cursor
  close cRefCursor;

  -- return ID or any other attribute(s)
  return rRecord.id; 

end;

【讨论】:

所有 4 个线程都生成 open cursor. No locks so far,当每个线程将 fetch cRefCursor into rRecord; 时,每个线程将锁定同一行,因为所有 4 个线程在第一次选择时得到相同的结果 例如我在表中有 5 个线程和 5 行。所有线程同时请求数据 - 每个线程进行选择并获取所有 5 行(有序),并且仅在第一行块之后。但是第二个线程尝试阻止来自相同数据的第一行... 他们得到的数据不同,他们得到的数据不同。这是“for update skip locked”的特性——测试一下。并且不要忘记将自动提交设置为 false,因为记录在提交时被解锁。 [72000][14551] ORA-14551: 无法在请求中执行 DML 操作。 ORA-06512:在“IP696.GET_NEXT_UNLOCKED_ID”,第 8 行 如果我删除这部分 for update skip locked 功能有效

以上是关于如何为 oracle 中的每个线程选择和阻止行?(PostgreSQL 有一个工作示例)的主要内容,如果未能解决你的问题,请参考以下文章

在 SQL 中,如何为每个组选择前 2 行

如何为oracle中的特定输入选择不包含空值的列名?

hive - 如何为每个匹配选择前 N 个元素

SQL:如何为一列中具有重复值的每组行选择一行?

如何为ggplot中的每个构面行添加y轴标题?

如何为 MySQL 中的每个组选择第一行?