Update the results of a SELECT statement

江枫思渺然 提交于 2020-01-01 03:19:09

问题


Oracle lets you update the results of a SELECT statement.

UPDATE (<SELECT Statement>)
SET <column_name> = <value>
WHERE <column_name> <condition> <value>;

I suppose that this could be used for updating columns in one table based on the value of a matching row in another table.

How is this feature called, can it efficiently be used for large updates, does it work when the SELECT joins multiple tables, and if so, how?


回答1:


I haven't seen a formal name for this. The Oracle SQL Reference just refers to updating a subquery. I tend to think of it as a form of "view updating", with the subquery being in in-line view.

Yes, it works when a number of tables are joined, but subject to the rules of view updating. This means that only one of the view's base tables can be updated, and this table must be "key-preserved" in the view: i.e. its rows should only be able to appear once in the view. This requires that any other tables in the view (subquery) are referenced via foreign key constraints on the table to be updated.

Some examples may help. Using the standard Oracle EMP and DEPT tables, with EMP.EMPNO being defined as the primary key of EMP, and EMP.DEPTNO being defined as a foreign key to DEPT.DEPTNO, then this update is allowed:

update (select emp.empno, emp.ename, emp.sal, dept.dname
        from   emp join dept on dept.deptno = emp.deptno
       )
set sal = sal+100;

But this is not:

-- DEPT is not "key-preserved" - same DEPT row may appear
-- several times in view
update (select emp.ename, emp.sal, dept.deptno, dept.dname
        from   emp join dept on dept.deptno = emp.deptno
       )
set dname = upper(dname);

As for performance: the optimiser will (must) identify the base table to be updated during parsing, and joins to other table will be ignored since they do not have any bearing on the update to be performed - as this AUTOTRACE output shows:

SQL> update (select emp.ename, emp.sal, dept.dname
  2              from   emp join dept on dept.deptno = emp.deptno
  3             )
  4      set sal = sal-1;

33 rows updated.


Execution Plan
----------------------------------------------------------
Plan hash value: 1507993178

------------------------------------------------------------------------------------
| Id  | Operation           | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT    |              |    33 |   495 |     3   (0)| 00:00:01 |
|   1 |  UPDATE             | EMP          |       |       |            |          |
|   2 |   NESTED LOOPS      |              |    33 |   495 |     3   (0)| 00:00:01 |
|   3 |    TABLE ACCESS FULL| EMP          |    33 |   396 |     3   (0)| 00:00:01 |
|*  4 |    INDEX UNIQUE SCAN| SYS_C0010666 |     1 |     3 |     0   (0)| 00:00:01 |
------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("EMP"."DEPTNO"="DEPT"."DEPTNO")

(Note that table DEPT is never accessed even though DEPT.DNAME appears in the subquery).




回答2:


The form you mention has no specific name AFAIK. Just updating the result of a select statement.

There is another form called Correlated update (with single or multicolumn update)

UPDATE TABLE(<SELECT STATEMENT>) <alias>
SET <column_name> = (
  SELECT <column_name>
  FROM <table_name> <alias>
  WHERE <alias.table_name> <condition> <alias.table_name>
);

The multicolumn form

...
SET (<column_name_list>) = (
  SELECT <column_name_list>
...

There is also a from which also returning of values called Update with returning clause

And some specifics for updates with nested tables. Best is to check at least this two pages

Oracle® Database SQL Language Reference SELECT

Oracle® Database SQL Language Reference UPDATE




回答3:


Thanks for comments, I thought this was standard Sql... :(

For Oracle you can write an update on a table where you retrieve information with a join like:

UPDATE (
    SELECT * 
    FROM table1 t1 
    LEFT JOIN table2 t2 ON t2.t1id = t1.ID
) SET t1.col1 = t2.col2

For Sql Server, it's:

UPDATE t1
SET col1 = t2.col2
FROM table1 t1
LEFT JOIN table2 t2 on t2.t1id = t1.id

If anyone knows a way to do this that works on Oracle, Sql Server and MySql I'd be interested.



来源:https://stackoverflow.com/questions/999812/update-the-results-of-a-select-statement

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!