同时更新两个表 - Oracle

时间:2015-11-30 20:17:27

标签: oracle

说我有两个表,例如:

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计划。

4 个答案:

答案 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> 
相关问题