说我有两个表,例如:
Product
prd_id prd_name parent_prd_id ...
123 Foo <null>
456 Bar <null>
789 Baz <null>
Product_Parent_Relation_Batch
prd_id prd_parent_id processed
555 888 T
123 789 F
456 789 F
我无法将关系数据直接加载到产品中。在孩子面前父母不存在的可能性。设计与否设计不佳,这就是它的方式。要更新产品表,我会执行相关更新,例如:
Oracle SQL: Update a table with data from another table
我想用父ID 789填充产品123和456,并将123和456设置为已处理=&#39; T&#39;在产品父关系批处理表中。好像我只能做一个。
我是否被迫在一个应用程序中执行此操作,在该应用程序中,我查询具有与现有产品记录匹配的prd_id的所有未处理批处理记录,为产品表执行一个单独的更新语句,为关系批处理表执行另一个,对于所有适用的记录?
CREATE OR REPLACE PROCEDURE sync_prd_with_parent
IS
cursor c1 is
select prd_id, parent_prd_id
from product_parent_relation_batch
inner join product on product_parent_relation_batch.prd_id = product.prd_id
where product_parent_relation_batch.processed = 'F';
BEGIN
FOR rec in c1
LOOP
UPDATE product SET parent_prd_id = rec.parent_prd_id WHERE prd_id = rec.prd_id;
UPDATE product_parent_relation_batch SET processed = 'T' WHERE product_parent_relation_batch.prd_id= rec.prd_id;
END LOOP;
END;
除非发布更好的建议,否则我将会参与上述PL / SQL计划。
答案 0 :(得分:3)
在物理上限制在同一查询中更新多个表。
针对此类场景的工作解决方案是创建应用程序 - PL / SQL或其他方法,以获取需要更新的两个表的信息,迭代结果,并在每次迭代中更新各个语句中的表。
答案 1 :(得分:2)
单一声明中无法做到这一点。即使使用可更新的连接,也只有一个表可以进行更改。
答案 2 :(得分:1)
重现并发会话导致的错误。
第一个会话执行产品更新:
08/12/2015 17:46:54:SQL> -- session 1
08/12/2015 17:47:12:SQL> BEGIN
2 UPDATE product pr
3 SET parent_prd_id =
4 (SELECT b.prd_parent_id
5 FROM product_parent_relation_batch b
6 INNER JOIN product p ON b.prd_id = p.prd_id
7 WHERE b.processed = 'F'
8 AND pr.prd_id = p.prd_id)
9 WHERE prd_id in (SELECT p.prd_id
10 FROM product_parent_relation_batch b
11 INNER JOIN product p ON b.prd_id = p.prd_id
12 WHERE b.processed = 'F');
13 END;
14 /
Procedimento PL/SQL concluído com sucesso.
在第二次更新发生之前,另一个会话会插入新行:
08/12/2015 17:47:31:SQL> -- session 2
08/12/2015 17:47:31:SQL> INSERT INTO product
2 VALUES (990, 'New', null);
1 linha criada.
08/12/2015 17:47:31:SQL> INSERT INTO product_parent_relation_batch
2 VALUES (990, 789, 'F');
1 linha criada.
08/12/2015 17:47:31:SQL>
08/12/2015 17:47:31:SQL> commit;
Commit concluído.
然后,使用这些新的提交行,我们的第一个事务更新批处理表:
08/12/2015 17:47:50:SQL> --- continues
08/12/2015 17:47:50:SQL> UPDATE product_parent_relation_batch pb
2 SET processed = 'T'
3 WHERE pb.prd_id IN (SELECT b.prd_id
4 FROM product_parent_relation_batch b
5 INNER JOIN product p ON b.prd_id = p.prd_id
6 WHERE b.processed = 'F'
7 AND pb.prd_id = p.prd_id);
3 linhas atualizadas.
08/12/2015 17:47:50:SQL> SELECT *
2 FROM product_parent_relation_batch b
3 INNER JOIN product p
4 ON b.prd_id = p.prd_id
5 WHERE p.prd_id = 990;
PRD_ID PRD_PARENT_ID P PRD_ID PRD PARENT_PRD_ID
---------- ------------- - ---------- --- -------------
990 789 T 990 New
08/12/2015 17:47:50:SQL> COMMIT;
Commit concluído.
注意3行已更新。通过检查现在具有“T”而不是“F”的“新”行来显示您提到的错误。
现在让我们尝试将其更改为Serializable Isolation Level:
08/12/2015 17:51:08:SQL> -- session 1
08/12/2015 17:51:24:SQL> BEGIN
2 SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
3 UPDATE product pr
4 SET parent_prd_id =
5 (SELECT b.prd_parent_id
6 FROM product_parent_relation_batch b
7 INNER JOIN product p ON b.prd_id = p.prd_id
8 WHERE b.processed = 'F'
9 AND pr.prd_id = p.prd_id)
10 WHERE prd_id in (SELECT p.prd_id
11 FROM product_parent_relation_batch b
12 INNER JOIN product p ON b.prd_id = p.prd_id
13 WHERE b.processed = 'F');
14 END;
15 /
Procedimento PL/SQL concluído com sucesso.
然后并发插入:
08/12/2015 17:50:59:SQL> -- session 2
08/12/2015 17:51:46:SQL> INSERT INTO product
2 VALUES (990, 'New', null);
1 linha criada.
08/12/2015 17:51:46:SQL> INSERT INTO product_parent_relation_batch
2 VALUES (990, 789, 'F');
1 linha criada.
08/12/2015 17:51:46:SQL>
08/12/2015 17:51:46:SQL> commit;
Commit concluído.
最后是第二次更新:
08/12/2015 17:51:24:SQL> --- continues
08/12/2015 17:52:16:SQL> UPDATE product_parent_relation_batch pb
2 SET processed = 'T'
3 WHERE pb.prd_id IN (SELECT b.prd_id
4 FROM product_parent_relation_batch b
5 INNER JOIN product p ON b.prd_id = p.prd_id
6 WHERE b.processed = 'F'
7 AND pb.prd_id = p.prd_id);
2 linhas atualizadas.
08/12/2015 17:52:16:SQL> SELECT *
2 FROM product_parent_relation_batch b
3 INNER JOIN product p
4 ON b.prd_id = p.prd_id
5 WHERE p.prd_id = 990;
não há linhas selecionadas
08/12/2015 17:52:16:SQL> COMMIT;
Commit concluído.
08/12/2015 17:52:16:SQL> SELECT *
2 FROM product_parent_relation_batch b
3 INNER JOIN product p
4 ON b.prd_id = p.prd_id
5 WHERE p.prd_id = 990;
PRD_ID PRD_PARENT_ID P PRD_ID PRD PARENT_PRD_ID
---------- ------------- - ---------- --- -------------
990 789 F 990 New
新行未受影响,因为Serializable隔离级别使其成为事务开始时的快照。
正确的版本与此类似:
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
UPDATE product pr
SET parent_prd_id =
(SELECT b.prd_parent_id
FROM product_parent_relation_batch b
INNER JOIN product p ON b.prd_id = p.prd_id
WHERE b.processed = 'F'
AND pr.prd_id = p.prd_id)
WHERE prd_id in (SELECT p.prd_id
FROM product_parent_relation_batch b
INNER JOIN product p ON b.prd_id = p.prd_id
WHERE b.processed = 'F');
UPDATE product_parent_relation_batch pb
SET processed = 'T'
WHERE pb.prd_id IN (SELECT b.prd_id
FROM product_parent_relation_batch b
INNER JOIN product p ON b.prd_id = p.prd_id
WHERE b.processed = 'F'
AND pb.prd_id = p.prd_id);
COMMIT;
END;
答案 3 :(得分:0)
您可以放弃光标并直接进行2次更新,如下所示:
SQL> create table product (prd_id,
2 prd_name,
3 parent_prd_id)
4 as
5 select 123, 'Foo', cast(null as number) from dual union all
6 select 456, 'Bar', null from dual union all
7 select 789, 'Baz', null from dual;
Table created
SQL> create table product_parent_relation_batch
2 (prd_id,
3 prd_parent_id,
4 processed)
5 as
6 select 555, 888, 'T' from dual union all
7 select 123, 789, 'F' from dual union all
8 select 456, 789, 'F' from dual;
Table created
SQL> SELECT p.prd_id, b.prd_id, prd_parent_id
2 FROM product_parent_relation_batch b
3 INNER JOIN product p
4 ON b.prd_id = p.prd_id
5 WHERE b.processed = 'F'
SQL> BEGIN
2 UPDATE product pr
3 SET parent_prd_id =
4 (SELECT b.prd_parent_id
5 FROM product_parent_relation_batch b
6 INNER JOIN product p ON b.prd_id = p.prd_id
7 WHERE b.processed = 'F'
8 AND pr.prd_id = p.prd_id)
9 WHERE prd_id in (SELECT p.prd_id
10 FROM product_parent_relation_batch b
11 INNER JOIN product p ON b.prd_id = p.prd_id
12 WHERE b.processed = 'F');
13 UPDATE product_parent_relation_batch pb
14 SET processed = 'T'
15 WHERE pb.prd_id IN (SELECT b.prd_id
16 FROM product_parent_relation_batch b
17 INNER JOIN product p ON b.prd_id = p.prd_id
18 WHERE b.processed = 'F'
19 AND pb.prd_id = p.prd_id);
20 COMMIT;
21 END;
22 /
PL/SQL procedure successfully completed
SQL> SELECT * FROM product_parent_relation_batch;
PRD_ID PRD_PARENT_ID PROCESSED
---------- ------------- ---------
555 888 T
123 789 T
456 789 T
SQL> SELECT * FROM product;
PRD_ID PRD_NAME PARENT_PRD_ID
---------- -------- -------------
123 Foo 789
456 Bar 789
789 Baz
SQL>