Skip to Main Content

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, MIchal.

Asked: August 03, 2016 - 12:49 pm UTC

Last updated: August 09, 2016 - 3:37 pm UTC

Version: Oracle Database 10g Release 10.2.0.5.0

Viewed 1000+ times

You Asked

Hi i was supposed to make script that will find data in table and based on these insert into same table.
When i was doing this operation while using temporary table to prevent mutating table error(it did not worked and i know my before insert trigger is poorly made caus i get this error).
Anyway i was getting error only while using

INSERT INTO table_name(columns....)
SELECT columns FROM other_table_name
WHERE id = 1

so i was trying to insert only one row at time and commit and it did not worked.

But simple
INSERT INTO table_name(columns...) VALUES(values...)

Was working without raising any error so whats the difference if i want to insert 1 row at time or 1 row at time?
I made my workaround iam just curious why is it like that.

Thanks for any answer.


Trigger code:
CREATE OR REPLACE TRIGGER TBIU_TRIG
 BEFORE INSERT OR UPDATE
 ON MY_TABLE
 FOR EACH ROW
DECLARE
  cursor c_max is
    select nvl(max(number1), 0) + 1
      from my_table;
begin
  if inserting then
    if :new.number1 is null then
      open c_max;
      fetch c_max into :new.number1;
      close c_max;
    end if;
end;


When i do like:
INSERT INTO my_table(col1) values('value');
Everything is OK. 1 Row was inserted.

But when i want to insert 1 row from another table where ID is PK like:
INSERT INTO my_table
SELECT col1
FROM another_table
WHERE id = 1;

I get following error cause of mutating table.

Test case:

CREATE TABLE my_table(col1 varchar2(30), number1 number(5));
CREATE TABLE another_table(col1 varchar2(30));
INSERT INTO another_table(col1) values('value123');

CREATE OR REPLACE TRIGGER TBIU_TRIG
BEFORE INSERT OR UPDATE
ON MY_TABLE
FOR EACH ROW
DECLARE
cursor c_max is
select nvl(max(number1), 0) + 1
from my_table;
begin
if inserting then
if :new.number1 is null then
open c_max;
fetch c_max into :new.number1;
close c_max;
end if;
end;

INSERT INTO my_table(col1) values('volue');
1 row inserted.

INSERT INTO my_table(col1)
SELECT col1 FROM another_table
Mutating table error.

This is my case summed up.





and Chris said...

Thanks for the test case. But please tell me your real code doesn't use "select max() + 1 from ...)"!

Use a sequence to assign incrementing numbers. Otherwise two concurrent sessions can insert the same number!

Anyway, onto your question.

When you have an insert like:

insert into t select ... from ...


Oracle doesn't know in advance how many rows this will insert. And if it inserts more than one, it could do so in any order.

If we allowed this, the values for "select max()" would be non-deterministic. In some executions row A might be first, so get the value 1. Others row B is first, so is 1.

This could lead to some unwanted, nasty side effects. So we disallow it.

But with a single row insert, Oracle knows that no one else has modified the table. Given the same starting state you'll always end up with the same result. It's deterministic. Thus allowed!

See also:

https://asktom.oracle.com/pls/apex/f?p=100:11:0%3A%3A%3A%3AP11_QUESTION_ID:9579487119866

Rating

  (1 rating)

Is this answer out of date? If it is, please let us know via a Comment

Comments

A reader, August 09, 2016 - 3:03 pm UTC

Hi, thanks for your answer....
Its not really my code its from my workplace and my company i work in is around already for some time so you can find there many and many "historical" codes taht none remember why it is like that and someone who wrote this prolly does not work there anymore :)
Anyway thanks for your answer it was helpful.
Chris Saxon
August 09, 2016 - 3:37 pm UTC

Ah, yes. Legacy code! No one wants to touch it, even if there are bugs!

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library