带有INSERT INTO的Oracle SQL- Lock行

时间:2014-01-20 16:05:13

标签: sql oracle plsql locking

我在Oracle SQL中遇到了以下问题。 gt_general2是一个全局临时表。

INSERT INTO gt_general2 (n_1, n_2, n_3)
select p.x_id, a.y_id, a.dollar_amt
from table_P p, table_A a
where p.x_id = a.x_id
  and a.h_date = i_date
  and a.dollar_amt > 0
for update of p.x_id, a.dollar_amt nowait; --this is the problem statement

当我尝试使用INSERT ... SELECT语法和SELECT ... FOR语法时,Oracle不喜欢。 我想在执行此插入时锁定行,因为真正的查询实际上相当复杂(并且对其他视图进行一些检查以确保存在父记录等)并且这样我可以在执行时插入到临时表中单行中的行锁定。

是否还有其他一些我想要的语法可以让我这样做?在概念层面上,我没有看到任何理由为什么Oracle不允许我从表P和A中选择行,在进程中锁定它们,并将值插入另一个表Z.毕竟我已经可以这样做了:

select p.x_id, a.y_id
BULK COLLECT into x_id_list, y_id_list
from table_P, table_A
where ...
for update nowait;

当然可以通过执行此查询两次锁定行 - 一次锁定,一次插入。以前,我这样做并使用锁定查询来检索数组中的“x_id”值以帮助INSERT INTO查询。但我遇到的问题是x_id值不足以识别我想要的行;我需要一对(x_id,y_id)并且无法创建新的嵌套表类型来存储它。

2 个答案:

答案 0 :(得分:3)

您无法在单个语句中将INSERTSELECT FOR UPDATE合并。但是,您可以使用常规SELECT FOR UPDATE并使用批量插入批量收集来实现几乎相似的性能。

如果在PL / SQL代码中声明集合,则也不需要创建新的永久SQL类型对象。

这是一个例子,首先是设置:

SQL> CREATE TABLE table_a (x_id NUMBER, y_id NUMBER, dollar_amt NUMBER);
Table created
SQL> CREATE TABLE table_p (x_id NUMBER);
Table created
SQL> INSERT INTO table_a VALUES (1, 1, 0);
1 row inserted
SQL> INSERT INTO table_a VALUES (2, 1, 10);
1 row inserted
SQL> INSERT INTO table_a VALUES (3, 1, 20);
1 row inserted
SQL> INSERT INTO table_p VALUES (2);
1 row inserted
SQL> INSERT INTO table_p VALUES (3);
1 row inserted
SQL> INSERT INTO table_p VALUES (4);
1 row inserted
SQL> CREATE GLOBAL TEMPORARY TABLE gt_general2(
  2     n_1 NUMBER, 
  3     n_2 NUMBER, 
  4     n_3 NUMBER);
Table created

锁定和插入程序(在11.2.0.2.0上测试):

SQL> DECLARE
  2     CURSOR cc IS
  3        SELECT p.x_id, a.y_id, a.dollar_amt
  4          FROM table_P p, table_A a
  5         WHERE p.x_id = a.x_id
  6           --AND a.h_date = i_date
  7           AND a.dollar_amt > 0
  8           FOR UPDATE OF p.x_id, a.dollar_amt NOWAIT;
  9     TYPE table_rec IS TABLE OF cc%ROWTYPE;
 10     l_table table_rec;
 11  BEGIN
 12     OPEN cc;
 13     LOOP
 14        -- bulk fetch
 15        FETCH cc BULK COLLECT INTO l_table LIMIT 100;
 16        EXIT WHEN l_table.count=0;
 17        -- bulk insert
 18        FORALL i IN 1..l_table.count
 19           INSERT INTO gt_general2 (n_1, n_2, n_3)
 20              VALUES (l_table(i).x_id,
 21                      l_table(i).y_id,
 22                      l_table(i).dollar_amt);
 23     END LOOP;
 24     CLOSE cc;
 25  END;
 26  /
PL/SQL procedure successfully completed

这将在一次通过中批量选择/插入100行。这应该比lock语句后跟一个insert语句更快。此外,您可以保证插入的行与锁定的行完全相同(而如果表已被修改,则一个接一个地执行的两个语句可能会返回不一致的结果)。

答案 1 :(得分:2)

这实际上取决于你为什么要这样做。

如果您需要访问事务开头的行中的值而不是每个查询开头的值,无论它们是否发生更改,那么我会考虑更改会话以进行更改读取一致性模式为“serializable”,而不是默认的“read committed”。

如果你真的要更新那些行,那么我会看一下使用公用表表达式(CTE)而不是全局临时表,将所有逻辑封装在一个查询中。在许多情况下,GTT和CTE相当于几乎相同的优化,尽管当然你不能通过一个过程对CTE进行分析,或者创建一个索引。