我在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)并且无法创建新的嵌套表类型来存储它。
答案 0 :(得分:3)
您无法在单个语句中将INSERT
与SELECT 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进行分析,或者创建一个索引。