0

I have written a computationally expensive select query which produces a certain result set. I need to insert this result into a table and the aggregates of this result set into another table. I need to do those in a single transaction instead of using two transactions as it can lead to inconsequential data issues.

Example scenario I have a table having data regarding employee name, department and salary

name department salary
Jason Finance 100
Bourne Finance 120
Michael Finance 90
Robert Sales 75
Downey Sales 500
Matthew Engineering 10
Tom Engineering 90

I want to insert data from this table into table A with sum of salary per department greater than 100. Into table B I want to insert a row for each department with sum of salary per department. Hence my table A would be like

name department salary
Jason Finance 100
Bourne Finance 120
Michael Finance 90
Robert Sales 75
Downey Sales 500

and table b would be like

department salary
Finance 310
Sales 575
Engineering 100

How can I do this in Oracle?

1
  • It is ideal if you can show what code you are starting with. Not only does this show good-faith effort on the part of a question author, but it can help answer authors tailor their answer to the specific area in which you were having trouble. Commented Oct 12, 2024 at 17:17

3 Answers 3

2

You simply need to control your transaction. When you do your first insert, you create a new transaction that does not auto-commit. It remains open. You can then do your second insert that does your aggregation. The transaction is still open - at any time you can roll it all back, or commit your work up to that point. There is no need to try to do it all in a single SQL statement. You only need to do it in a single transaction, which can have many SQL statements.

In PL/SQL:

BEGIN
  INSERT INTO table_A....;
 
  INSERT INTO table_B.... GROUP BY ....;

  COMMIT; -- will only commit work if both table_A and table_b succeeded
EXCEPTION
  WHEN OTHERS THEN
    -- report somehow, like dbms_output.put_line(SQLERRM) or log error in a log table
    ROLLBACK; -- undoes work on table_A if table_B failed.
END;

Of course, if you are using an ad-hoc SQL script in a database client software of some kind and not submitting a PL/SQL block like I've shown here, then you may have auto-commit configured in your software's options/settings/preferences (it's controlled by the client, not the database). The solution then is either to use PL/SQL like my example above, or to change your software not to auto-commit and handle the committing or rolling back yourself.

Sign up to request clarification or add additional context in comments.

Comments

0

You could use INSERT ALL

INSERT ALL 
  INTO table1 (<cols to insert 1>) 
    VALUES (<cols from result>) 
  INTO table2 (<cols to insert 2>) 
    VALUES (< cols from result>) 
  WITH dat AS ( 
    < your data >
  ) 
  SELECT * FROM dat;

for reference.

3 Comments

Thank you for the suggestion. The query is taking me in the right direction but for table two i need to insert aggregate data of the original dataset, instead of redistributing data into multiple tables
You can do both in one place. In the WITH clause calculate the aggregates and just use OVER and PARTITION BY to have them in the same rows and then just select the respective columns for each table
I have modified the question with a sample requirement. Would you mind helping me with a sample code for this. Greatly appreciate help in advance
0

You can use:

BEGIN
  INSERT INTO a (name, department, salary)
  SELECT name, department, salary
  FROM   (
    SELECT name, department, salary,
           SUM(salary) OVER (PARTITION BY department) AS dept_salary
    FROM   table_name
  )
  WHERE  dept_salary > 100;

  INSERT INTO b (department, salary)
  SELECT department, SUM(salary) FROM table_name GROUP BY department;

  COMMIT;
END;
/

Which, for the sample data:

CREATE TABLE table_name (name, department, salary) AS
SELECT 'Jason',   'Finance',     100 FROM DUAL UNION ALL
SELECT 'Bourne',  'Finance',     120 FROM DUAL UNION ALL
SELECT 'Michael', 'Finance',      90 FROM DUAL UNION ALL
SELECT 'Robert',  'Sales',        75 FROM DUAL UNION ALL
SELECT 'Downey',  'Sales',       500 FROM DUAL UNION ALL
SELECT 'Matthew', 'Engineering',  10 FROM DUAL UNION ALL
SELECT 'Tom',     'Engineering',  90 FROM DUAL;

CREATE TABLE a AS
SELECT * FROM table_name WHERE 1 = 0;

CREATE TABLE b (department, salary) AS
SELECT department, salary FROM table_name WHERE 1 = 0;

Then after the INSERTs, table A contains:

NAME DEPARTMENT SALARY
Bourne Finance 120
Michael Finance 90
Jason Finance 100
Downey Sales 500
Robert Sales 75

and table B contains:

DEPARTMENT SALARY
Sales 575
Engineering 100
Finance 310

fiddle

1 Comment

Currently, instead of table_name i have a result set from a computational expensive query. How can i use this result to insert into first table and its aggregates into second table. I dont want to insert into a temporary table. I want both these inserts to happen in a single transaction

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.