同时更新两个表 - Oracle

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34007216/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 00:59:58  来源:igfitidea点击:

Updating two Tables Simultaneously - Oracle

oracle

提问by jiman

Say I have two tables such as:

假设我有两个表,例如:

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

I can't load the relation data directly into product. There's a chance of the parent not existing before the child. Poor design or not, that's the way it is. To update the product table, I'd do a correlated update like:

我无法将关系数据直接加载到产品中。父母有可能在孩子之前不存在。糟糕的设计与否,就是这样。要更新产品表,我会做一个相关的更新,如:

Oracle SQL: Update a table with data from another table

Oracle SQL:用另一个表中的数据更新一个表

I want to populate products 123 and 456 with the parent id 789 and set 123 and 456 to processed = 'T' in the product parent relation batch table. Seems like I can only do one.

我想用父 ID 789 填充产品 123 和 456,并在产品父关系批处理表中将 123 和 456 设置为 processing = 'T'。好像只能做一个。

Am I forced to do this in an application, where I query for all non-processed batch records that have a matching prd_id with an existing product record, execute one individual update statement for the product table and another for the relation batch table, for all applicable records?

我是否被迫在应用程序中执行此操作,在其中查询所有具有匹配 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;

I'm going to settle for the above PL/SQL program unless a better suggestion is posted.

除非发布更好的建议,否则我将接受上述 PL/SQL 程序。

回答by jiman

One is physically restricted from updating multiple tables in the same query.

一种是物理上限制更新同一查询中的多个表。

A working solution for this kind of scenario is to create an application - PL/SQL or otherwise, to grab information for both tables you need to update, iterate through the results, and update the tables in individual statements in each iteration.

对于这种场景,一个可行的解决方案是创建一个应用程序 - PL/SQL 或其他方式,获取需要更新的两个表的信息,迭代结果,并在每次迭代中更新各个语句中的表。

回答by ibre5041

There is no way how to do that in a single statement. Even when using update-able joins, only one table can be subject of the change.

没有办法在单个语句中做到这一点。即使使用可更新的连接,也只有一个表可以被更改。

回答by Francisco Sitja

Reproducing the error caused by concurrent sessions.

重现并发会话导致的错误。

First session executes the Update on Product:

第一个会话执行产品更新:

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.

Before the 2nd update happens a different session inserts new rows:

在第二次更新发生之前,不同的会话插入新行:

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.

Then, with those new commited rows, our first transaction Updates the Batch table:

然后,使用这些新提交的行,我们的第一个事务更新批处理表:

    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.

Notice 3 rows are updated. The error you mentioned is displayed by checking that "New" row that now has 'T' instead of 'F'.

注意 3 行已更新。您提到的错误是通过检查现在有“T”而不是“F”的“新”行来显示的。

Now let's try it changing it to Serializable Isolation Level:

现在让我们尝试将其更改为 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.

Then concurrent insert:

然后并发插入:

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.

And finally the 2nd update:

最后是第二次更新:

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                                                                                                                                                                            

The new row is untouched, because the Serializable isolation level makes it a snapshot at the beginning of the transaction.

新行未受影响,因为 Serializable 隔离级别使其成为事务开始时的快照。

The correct version would be similar to this:

正确的版本类似于:

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;

回答by Francisco Sitja

You could forego the cursor and go straight for the 2 updates as follows:

您可以放弃光标并直接进行 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>