Two DMLs based on Same Subquery - oracle

I need to cancel some orders, and then insert a row into another table, both based on the same subquery.
There is a very small chance that the subquery will return different rows between the time that the 1st and 2nd DMLs are issued.
But is there a proper way to do this such the orders updated are the same orders that are inserted into the cancellations table?
I am using Oracle and JDBC. Thanks.
update orders
set status = 'cancel'
where order_number in (select order_number from some_other_table_where...)
insert into order_cancellations
select order_number select order_number from some_other_table_where...

Here are five approaches that spring to mind:
(1) Put a trigger on the orders table so whenever the status is set to 'cancel', a row is inserted into order_cancellations.
(2) Use the returning clause in insert. Do the insert first and use this information for the update.
(3) Add a creation date to order_cancellations and do this insert first. Then update orders using the just-inserted rows
(4) Wrap the two statements in a transaction (this might require locking the other tables).
(5) Load the subquery data into a temporary table and use that table for both operations.
I also wonder if you could eliminate the need for the cancellations table just by having a cancellation_date column in the orders table.

Related

Adding a sequence to a large Oracle table

I have queries that take an existing large table and build tables off of them for reporting. The problem is that the source tables are 60-80MM+ records and it takes a long time to recreate. I'd like to be able to identify which records are new so I can build just add the new records to the reporting tables.
To me, the best way to identify this is to have an identity column. Is there any significant cost to creating this and adding it to the table?
Separately, is it possible to create a materialized view that takes data from one of these tables but add a sequence as part of the materialized view? That is, something like
create materialized view some_materialized_view as
select somesequence.nextval, source_table.*
from source_table?
You can add a sequence based column to your table, but as Gary suggests I wouldn't do that.
The task you are about to solve is so common that other solutions have been already implemented.
The first built-in option that comes to mind is the system change number SCN, a kind of Oracle internal clock. By default, tables are set up to record the SCN of the whole (usually 8K) block, containing usually many rows, but you can set a table to keep a record of the SCN that changed every row. Then you can track the columns that are new or change and have not been copied to your reporting tables.
CREATE TABLE t (c1 NUMBER) ROWDEPENDENCIES;
INSERT INTO t VALUES (1);
COMMIT;
SELECT c1, ora_rowscn FROM t;
Secondly, I would think of adding a date column. With 60-80 mio rows I wouldn't do this with ALTER TABLE xxx ADD (d DATE DEFAULT SYSDATE), but with rename, create as select, drop:
CREATE TABLE t AS SELECT * FROM all_objects;
RENAME t TO told;
CREATE TABLE t AS SELECT sysdate AS d, told.* FROM told;
ALTER TABLE t MODIFY d DATE DEFAULT SYSDATE;
DROP TABLE told;
Thirdly, I would read up on materialized views. I never had the chance to use this a work, but in theory, you should be able to set up a materialized view log on your 80 m table that records changes and updates dependent materialized views.
And forthly, I'd look into partitioning your large table on the (newly introduced) date column, so that identifying the new rows will become faster. That sadly depends on your version and Oracle license, though.

Oracle update table value returned 'single-row sub-query returned more than one row '

I wanna add a column named 'Bonus_AMT' on table 'Employee'.
Here's the clause I wrote.
enter image description here
I ran the above clause, but it didn't work. It returns that 'single-row' sub-query return more than one row. How could I solve that?
You need to join table in update clause with table in select clause used in SET. Here is the example.
update employees e1 set bonus = (select salary*commission_pct from employees e2 where e1.employee_id = e2.employee_id);
You need to make sure that both versions of tables are joined on primary key.
Your select query clause used in set is returning multiple row for the applied date range. So you need to change that select clause so that at a time it will single record and update the same record using joins.

Oracle Trigger with update view and after insertion of rows

I want to fire a trigger whenever I insert/delete rows in 2 tables belonging to two different schema. I have a sql query as:
select n.name as name, count(distinct d.projects) as count
from emp n, dep d
where n.dep=d.dep
group by n.name;
When I insert or delete rows from these tables emp and dep, I want to fire a trigger automatically to update view containing the above sql query.
I am using Oracle Database. Please advise me how to do that.

Conditional Insert or Update in Oracle

I have one table in oracle where data gets inserted from some third party. I want to populate master tables from that table. So, what will be the best way performance wise using collection.
E.g. Suppose, the table into which data will get populated from third party is 'EMP_TMP'.
Now I want to populate 'EMPLOYEE' master table through procedure which will get populated from EMP_TMP Table.
Here again there is one condition like IF SAME EMPID (this is not primary key) EXISTS then we have to UPDATE FULL TABLE which consists of SAME EMPID ELSE we have INSERT NEW RECORD.
[Note: Here EMPID is VARCHAR2 and EMPNO will be primary key where we will use SEQUENCE]
I think here merge will not perform much better performancewise since we cant use collection in MERGE statement.
Well, if performance is your primary consideration, and you don't like MERGE, then how about this (run as script, single transaction):
delete from EMPLOYEE where emp_id IN (
select emp_id from EMP_TMP);
insert into EMPLOYEE
select * from EMP_TMP;
commit;
Obviously not the "safest" approach (and as written assumes exact same table definitions and you have the rollback), but should be fast (you could also mess with IN vs EXISTS etc). And I couldn't quite understand your post if emp_id or emp_no was the common key in these 2 tables, but use whichever makes sense in your situation.
Create a procedure, you need to be using PL/SQL.
Do an update first then test sql%rowcount.
If it is 0, no updates where done and you have to do an insert instead.
I think that this is fairly efficient.
pseudo code
Update table;
if sql%rowcount = 0 then
//get new sequence number
insert into table;
END IF;
COMMIT;
HTH
Harv

Assign auto-incrementing value to new column in Oracle

I have this table in an Oracle DB which has a primary key defined on 3 of the data columns. I want to drop the primary key constraint to allow rows with duplicate data for those columns, and create a new column, 'id', to contain an auto-incrementing integer ID for these rows. I know how to create a sequence and trigger to add an auto-incrementing ID for new rows added to the table, but is it possible to write a PL/SQL statement to add unique IDs to all the rows that are already in the table?
Once you have created the sequence:
update mytable
set id = mysequence.nextval;
If you're just using an integer for a sequence you could update the id with the rownum. e.g.
update
table
set id = rownum
You then need to reset the sequence to the next valid id.
Is this what you need?
UPDATE your_table
SET id = your_seq.nextval;
This assumes you don't care what order your primary keys are in.
First you should check your PCTFREE... is there enough room for every row to get longer?
If you chose a very small PCTFREE or your data has lots of lenght-increasing updates, you might begin chaining every row to do this as an update.
You almost certainly better to do this as a CTAS.
Create table t2 as select seq.nextval, t1.* from t1.
drop t1
rename t2 to t1.

Resources