3

Can I make an update using common table expression in oracle database?

I am getting error ORA-00928: missing SELECT keyword when I am trying this:

with average as (SELECT avg(salary) FROM instructor)
update instructor
               set salary = case
                   when salary <= average then salary * 1.05 
                   else salary * 1.03                                     
               end
Vadzim
  • 24,954
  • 11
  • 143
  • 151
shashankgaurav
  • 65
  • 1
  • 1
  • 9
  • I do not have much experience with Oracle database, but I think you're getting an error because you want to use a CTE (that returns results in the form of a table) inside an update. Why don't you just store the result of `SELECT avg(salary) FROM instructor` into a variable and use it in the UPDATE? I think it's the simplest way to do what you want.. – Alberto Solano Mar 04 '15 at 07:51
  • 1
    Okay I will do that. But i have a question can we use with and update clause together? – shashankgaurav Mar 04 '15 at 07:53
  • As I said, a [CTE](http://en.wikipedia.org/wiki/Hierarchical_and_recursive_queries_in_SQL#Common_table_expression) returns results in the form of a table, and then you can use it through JOINs with other tables, or use in other ways (but not in the way you're using it) in order to have the results you want. So, to answer your question: No, you cannot use a CTE (it's not named 'with') in that way like yours. – Alberto Solano Mar 04 '15 at 08:01
  • 1
    It is syntactically incorrect. Yes, you can use WITH clause in an update statement, See my answer. – Lalit Kumar B Mar 04 '15 at 08:24

3 Answers3

6

Can I do something like this in oracle database?

Well, it is not about whether you could do it or not. It is about whether you need to do it or not. In your query I don't see any filter criteria. You want to update all the rows? I don't see a need of CTE in your case.

When do you need a CTE, i.e. a with clause as a sub-query factoring method whenever you have a scenario where the sub-query is executed multiple times. You use a WITH clause to make sure the subquery is executed once, and the resultset is stored as a temp table.

Yes, you could use WITH clause for an UPDATE statement.

For example,

UPDATE TABLE t
SET t.column1, t.column2 = (SELECT column1, column2 FROM 
                                       (
                                        WITH cte AS(
                                   SELECT ... FROM another_table
                                                 )
                                         SELECT * FROM cte
                                        )

You could use a MERGE statement USING the WITH clause.

For example,

SQL> MERGE INTO emp e USING
  2  (WITH average AS
  3    (SELECT deptno, AVG(sal) avg_sal FROM emp group by deptno)
  4  SELECT * FROM average
  5  ) u
  6  ON (e.deptno = u.deptno)
  7  WHEN MATCHED THEN
  8  UPDATE SET e.sal      =
  9    CASE
 10      WHEN e.sal <= u.avg_sal
 11      THEN e.sal * 1.05
 12      ELSE e.sal * 1.03
 13    END
 14  /

14 rows merged.

SQL>
Lalit Kumar B
  • 47,486
  • 13
  • 97
  • 124
3

Since average salary just a scalar value you can do

update instructor
   set salary = case
       when salary <= (select avg(t.salary) from instructor t) then salary * 1.05 
       else salary * 1.03                                     
   end

In that case Oracle first compute the average (say 1234.4567) and then perform the update.

Vadzim
  • 24,954
  • 11
  • 143
  • 151
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
2

Basing on another answer on correlated update of key-preserved view, here is another possible option to use CTE with update in Oracle SQL avoiding duplication of where clause:

update (
    with cte as (select avg(salary) average_salary from instructor)
    select id, salary, cte.average_salary from instructor cross join cte
    where <some_condition>
)
set salary = case
    when salary <= average_salary/2 then salary * 1.1 
    when salary <= average_salary then salary * 1.05 
    else salary * 1.03                                     
end

In case of self-join this can be simplified to CTE-less version:

update (
    select id, salary, (select avg(salary) from instructor) average_salary 
    from instructor
    where <some_condition>
)
set salary = case
    when salary <= average_salary/2 then salary * 1.1 
    when salary <= average_salary then salary * 1.05 
    else salary * 1.03                                     
end
Community
  • 1
  • 1
Vadzim
  • 24,954
  • 11
  • 143
  • 151
  • Here is more on "key preserved" thing: http://dba.stackexchange.com/questions/38728/oracle-non-key-preserved-table-should-be – Vadzim Oct 13 '16 at 20:38