Trigger for only changed values - oracle

Say we have 3 records in table: orig_tab
---------------------------------------------
| PK | Name | Address | Postal Code |
---------------------------------------------
| 1 | AA | Street1 | 11111 |
| 2 | BB | Street2 | 22222 |
| 3 | CC | Street3 | 33333 |
---------------------------------------------
Now the data is changed:
---------------------------------------------
| PK | Name | Address | Postal Code |
---------------------------------------------
| 1 | AA | Street1 | 11111 |
| 2 | BB | Street2 | 44444 |
| 3 | CC | Dtreet7 | 33333 |
---------------------------------------------
What client wants is the update records and only the updated columns (yes, I know it doesn't make any sense but they use some old system from 1970s and they want to do some logging etc.). So the reporting table should be like this:
---------------------------------------------
| PK | Name | Address | Postal Code |
---------------------------------------------
| 2 | | | 44444 |
| 3 | | Dtreet7 | |
---------------------------------------------
This what I tried:
CREATE OR REPLACE TRIGGER vr_reporting_trigger
AFTER UPDATE ON orig_tab
FOR EACH ROW
BEGIN
IF inserting THEN
INSERT INTO rep_tab(pk, name, address, code)
SELECT :new.pk, :new.name, :new.address, :new,code FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM rep_tab WHERE pk = :new.pk);
UPDATE rep_tab t SET t.name = :new.name, t.address = :new.address, t.code = :new.code
WHERE t.pk = :new.pk;
ELSIF updating THEN
IF :new.pk <> :old.pk THEN
UPDATE rep_tab t
SET t.name = :new.name, t.address = :new.address, t.code =: new.code
WHERE t.pk = :old.pk ;
END IF;
MERGE INTO rep_tab d
USING DUAL ON (d.pk = :old.pk)
WHEN MATCHED THEN
UPDATE SET d.name = :new.name, d.address = :new.address, d.code =: new.code
WHEN NOT MATCHED THEN
INSERT (d.pk,d.name, d.address, d.code) VALUES (:new.pk,:new.name, new.address, new.code);
END IF;
END;
with this solution, I get:
---------------------------------------------
| PK | Name | Address | Postal Code |
---------------------------------------------
| 2 | BB | Street2 | 44444 |
| 3 | CC | Dtreet7 | 33333 |
---------------------------------------------
I know that it somewhere in insert claus in when updating statement but I can't figure out how to have this claus changed as per my requirement. Any suggestion?
Thanks in advance.

You need this:
In an UPDATE trigger, a column name can be specified with an UPDATING
conditional predicate to determine if the named column is being
updated. For example, assume a trigger is defined as the following:
CREATE OR REPLACE TRIGGER ...
... UPDATE OF Sal, Comm ON Emp_tab ...
BEGIN
... IF UPDATING ('SAL') THEN ... END IF;
END;
From Oracle documentation(9i)
11gR2 documentation

Trigger for only changed values.
As an example (only NOT NULL support) (comparison operators with NULL always return FALSE):
CREATE OR REPLACE TRIGGER trigger_department_update
BEFORE UPDATE OF
department_id,
cluster_id,
division_id,
macroregion_id,
address,
email
ON cl_department
FOR EACH ROW
BEGIN
IF (
:new.department_id != :old.department_id
OR :new.cluster_id != :old.cluster_id
OR :new.division_id != :old.division_id
OR :new.macroregion_id != :old.macroregion_id
OR :new.address != :old.address
OR :new.email != :old.email
)
THEN :new.update_date := CURRENT_TIMESTAMP;
END IF;
END;
With NULL support (Thanks to #Oleg in the comments below this post) (Thanks to #Chris Bandy for the answer):
CREATE OR REPLACE TRIGGER trigger_department_update
BEFORE UPDATE OF
department_id,
cluster_id,
division_id,
macroregion_id,
address,
email
ON cl_department
FOR EACH ROW
BEGIN
IF (
((:new.department_id <> :old.department_id OR :new.department_id IS NULL OR :old.department_id IS NULL) AND NOT (:new.department_id IS NULL AND :old.department_id IS NULL))
OR ((:new.cluster_id <> :old.cluster_id OR :new.cluster_id IS NULL OR :old.cluster_id IS NULL) AND NOT (:new.cluster_id IS NULL AND :old.cluster_id IS NULL))
OR ((:new.division_id <> :old.division_id OR :new.division_id IS NULL OR :old.division_id IS NULL) AND NOT (:new.division_id IS NULL AND :old.division_id IS NULL))
OR ((:new.macroregion_id <> :old.macroregion_id OR :new.macroregion_id IS NULL OR :old.macroregion_id IS NULL) AND NOT (:new.macroregion_id IS NULL AND :old.macroregion_id IS NULL))
OR ((:new.address <> :old.address OR :new.address IS NULL OR :old.address IS NULL) AND NOT (:new.address IS NULL AND :old.address IS NULL))
OR ((:new.email <> :old.email OR :new.email IS NULL OR :old.email IS NULL) AND NOT (:new.email IS NULL AND :old.email IS NULL))
)
THEN :new.update_date := CURRENT_TIMESTAMP;
END IF;
END;

to treat null :
... nvl(to_char(:new.department_id,' ') <> nvl(to_char(:old.department_id,' ') OR ...

Related

How to create a single oracle procedure to perform multiple query task

Actually, I want to create an oracle procedure to normalize the table and for that we required a multiple queries/task to perform /execute. Below are the steps for which will help to write an oracle procedure:
step 1) Suppose a table name temp and there are nearly millions of records in it. So get the distinct records from whole table and
insert into same table by updating value for ver_id column as -1. Below is the example and table structure to understand
| pid | address | key |ver_id
| 1 | 242 Street | 123 | 1
| 2 | 242 Street | 123 |2
| 3 | 242 Street | 123 |3
| 4 | 242 Long St | 456 |4
Expected Resultis below :
select 2 distinct records for duplicate records from above table and insert that distinct record at the end of the table by updating ver_id as -1 like below:
| pid | address | key |ver_id
| 1 | 242 Street | 123 | 1
| 2 | 242 Street | 123 |2
| 3 | 242 Street | 123 |3
| 4 | 242 Long St | 456 |4
| 5 | 242 Street | 123 |-1
step 2) Find all parents records of given key in ADDRESS_TEMP table and update them with
pid (primary key of temp table) which is newly created in step 1 i.e pid as 5 and 6
ADDRESS_TEMP table (parent of temp table)
pid is the primary key of temp table and foreign key of ADDRESS_TEMP table
|addr_id | ver_id | pid
| 11 | 1 | 1
| 12 | 2 | 2
| 13 | 3 | 3
| 14 | 4 | 4
| 15 | 5 | 5
| 16 | 6 | 6
After Update
|addr_id | ver_id | pid
| 11 | 1 | 1
| 12 | 2 | 2
| 13 | 3 | 3
| 14 | 4 | 4
| 15 | 5 | 5
| 15 | 6 | 6
| 15 | 7 | 7
step 3): Delete all temp table records where key is 123 and 456 (in short delete all duplicate records) whose ver_id is not equal to -1
so the expected result of temp table is like below:
| pid | address | key |ver_id
| 4 | 242 Long St | 456 |4
| 5 | 242 Street | 123 |-1
For the first step ,exeecuted this query as below and its working as expected buti want to perfrom all the above steps in one single procedure .
insert into temp (id, address, key, ver_id)
with data as
(select t.*,
row_number() over (partition by address, key order by id) rn
from temp t
),
data2 as
(select distinct d.address, d.key
from data d where d.rn > 1
)
select seq_temp.nextval, address, key, -1
From data2.`
But above one is just for the step first but we need to create a procedure for all the above 3 steps and also i have tried to add the above insert statement into the procedure and it gets executed but for step2 and 3 it needs to be created
CREATE OR replace PROCEDURE p1 AS
cursor c_temp IS
SELECT * FROM temp ;
r_temp c_temp%ROWTYPE;
BEGIN
OPEN c_temp;
LOOP
FETCH c_temp INTO r_temp;
EXIT WHEN c_temp%NOTFOUND;
insert into temp (id, address, key, ver_id)
with data as
(select t.*,
row_number() over (partition by address, key order by id) rn
from temp t
),
data2 as
(select distinct d.address, d.key
from data d where d.rn > 1
)
select seq_temp.nextval, address, key, -1
From data2;
END LOOP;
CLOSE c_temp;
END;
Your instructions are quite confusing and I'm not sure that I got it completely. Anyway, below is the answer (with comments in the code). Even If it isn't exactly what you wanted - the logic and the structure of the code should be ok. You will have to adjust it to your context anyyway.
Table temp data before and after:
/* TEMP before
P_ID ADDRESS A_KEY VER_ID
1 242 Street 123 1
2 242 Street 123 2
3 242 Street 123 3
4 242 Long St 456 4
TEMP after
P_ID ADDRESS A_KEY VER_ID
4 242 Long St 456 4
5 242 Street 123 -1 */
Table TEMP_ADDRESS before and after:
/* TEMP_ADDRESS before
ADDR_ID VER_ID P_ID
11 1 1
12 2 2
13 3 3
14 4 4
TEMP_ADDRESS after
ADDR_ID VER_ID P_ID
11 1 5
12 2 5
13 3 5
14 4 4 */
Here is the code with comments...
SET SERVEROUTPUT ON
DECLARE
-- Declare cursor to fetch you distinct records from table TEMP
CURSOR c_temp IS SELECT Count(*) "CNT", ADDRESS, A_KEY FROM temp WHERE VER_ID > 0 GROUP BY ADDRESS, A_KEY HAVING Count(*) > 1 ORDER BY A_KEY;
-- ------------- Cursor records below ----------------
-- CNT ADDRESS A_KEY
-- ------ ------------ ----------
-- 3 242 Street 123
-- ----------------------------------------------------
cSet c_temp%ROWTYPE;
sq VarChar2(1) := ''''; -- single quote character (4 single quotes) -- using it to construct Sql commands
mSql VarChar2(512) := '';
mID TEMP.P_ID%TYPE;
mAddr TEMP.ADDRESS%TYPE;
mKey TEMP.A_KEY%TYPE;
BEGIN
Select Nvl(Max(P_ID), 0) Into mID From TEMP; -- Last (max) P_ID from taable TEMP --> 4 (empty table would return 0)
--
OPEN c_temp;
LOOP
FETCH c_temp INTO cSet;
EXIT WHEN c_temp%NOTFOUND;
mID := mID + 1; -- add 1 to mID for insert into TEMP
-- constructing INSERT commands for every cursor record
mSql := 'INSERT INTO TEMP(P_ID, ADDRESS, A_KEY, VER_ID) VALUES(' || mID || ', ' || sq || cSet.ADDRESS || sq || ', ' || cSet.A_KEY || ', -1)';
--
-- --------------- constructed command(s) for cursor record(s) ---------------------------------
-- INSERT INTO TEMP(P_ID, ADDRESS, A_KEY, VER_ID) VALUES(5, '242 Street', 123, -1)
--
Execute Immediate mSql; -- execute created INSERT command(s)
--
-- While you still have m_ID and cursor record's ADDRESS and A_KEY use them to update parent table TEMP_ADDRESS
Begin
UPDATE TEMP_ADDRESS SET P_ID = mID WHERE P_ID IN(Select P_ID FROM TEMP WHERE ADDRESS = cSet.ADDRESS And A_KEY = cSet.A_KEY And VER_ID > 0);
-- after that you can delete records from table temp that are not needed any more
DELETE FROM TEMP WHERE P_ID IN(Select P_ID FROM TEMP WHERE ADDRESS = cSet.ADDRESS And A_KEY = cSet.A_KEY And VER_ID > 0);
Commit;
Exception
WHEN OTHERS THEN
Rollback; -- if anything went wrong - Rollback and send a message
DBMS_OUTPUT.PUT_LINE('ERR - UPDATE or DELETE ' || Chr(10) || SQLERRM);
End;
--
END LOOP;
CLOSE c_temp;
Exception
WHEN OTHERS THEN
Rollback; -- if anything went wrong - Rollback and send a message
DBMS_OUTPUT.PUT_LINE('ERR - INSERT ' || Chr(10) || SQLERRM);
END;
Regards...

monetdb full outer join resulting in varchar type_digits=0

I am using MonetDB v11.29.7 "Mar2018-SP1" on a Windows10 x64 bit operating system. When I perform a full outer join with two tables on respective varchar columns with lengths > 0 (type_digits > 0), the resultant column in the target table yields a varchar column with type_digits=0, although the column data seems to display the proper, non-null varchar records.
I am not sure how to interpret column information of type=varchar and type_digits=0. This state is causing issues in the subsequent handling/extraction of data via Python interfaces (UDFs), as the expected Python dtype for the data of this column is ambiguous for Python numpy conversion.
I have provided a simple example whereby I created two small tables (dummy4 and dummy5) with two columns each and then create a third table (dummy6) using a full outer join command.
For table dummy6 and column "key", I would have expected the type_digits=32 (as per the "key" columns in the two source tables dummy4 & dummy5). Additionally, how should I interpret type=varchar and type_digits=0 state? What would be the proper handling/expectation when accessing/allocating a Python/numpy array for extracting the "key" column of table "dummy6" (via Python UDFs) in this case?
create table dummy4(key varchar(32), val int);
insert into dummy4 values('AAAAAAAA',1);
insert into dummy4 values('BBBBBBBBB',2);
select * from dummy4;
+-----------+------+
| key | val |
+===========+======+
| AAAAAAAA | 1 |
| BBBBBBBBB | 2 |
+-----------+------+
create table dummy5(key varchar(32), val int);
insert into dummy5 values('CCCCCCCC',3);
insert into dummy5 values('DDDDDDDD',4);
select * from dummy5;
+----------+------+
| key | val |
+==========+======+
| CCCCCCCC | 3 |
| DDDDDDDD | 4 |
+----------+------+
create table dummy6 as select key, dummy4.val as "val4", dummy5.val as "val5" from dummy4 full outer join dummy5 using (key);
select * from dummy6;
+-----------+------+------+
| key | val4 | val5 |
+===========+======+======+
| AAAAAAAA | 1 | null |
| BBBBBBBBB | 2 | null |
| CCCCCCCC | null | 3 |
| DDDDDDDD | null | 4 |
+-----------+------+------+
select t.name as "table_name", t.id as "table_id", c.id as "column_id", c.name as "column_name", c.type, c.type_digits from sys.tables t JOIN sys.columns c ON c.table_id = t.id where t.name = 'dummy4';
+------------+----------+-----------+-------------+---------+-------------+
| table_name | table_id | column_id | column_name | type | type_digits |
+============+==========+===========+=============+=========+=============+
| dummy4 | 78445 | 78443 | key | varchar | 32 |
| dummy4 | 78445 | 78444 | val | int | 32 |
+------------+----------+-----------+-------------+---------+-------------+
select t.name as "table_name", t.id as "table_id", c.id as "column_id", c.name as "column_name", c.type, c.type_digits from sys.tables t JOIN sys.columns c ON c.table_id = t.id where t.name = 'dummy5';
+------------+----------+-----------+-------------+---------+-------------+
| table_name | table_id | column_id | column_name | type | type_digits |
+============+==========+===========+=============+=========+=============+
| dummy5 | 78449 | 78447 | key | varchar | 32 |
| dummy5 | 78449 | 78448 | val | int | 32 |
+------------+----------+-----------+-------------+---------+-------------+
select t.name as "table_name", t.id as "table_id", c.id as "column_id", c.name as "column_name", c.type, c.type_digits from sys.tables t JOIN sys.columns c ON c.table_id = t.id where t.name = 'dummy6';
+------------+----------+-----------+-------------+---------+-------------+
| table_name | table_id | column_id | column_name | type | type_digits |
+============+==========+===========+=============+=========+=============+
| dummy6 | 78457 | 78454 | key | varchar | 0 |
| dummy6 | 78457 | 78455 | val4 | int | 32 |
| dummy6 | 78457 | 78456 | val5 | int | 32 |
+------------+----------+-----------+-------------+---------+-------------+
In fact this was a MonetDB's bug and was fixed today. Th fix will be featured on the upcoming Nov2019 release.

Oracle plan_baseline is ignored

I have query with bind variables which comming from outer application.
The optimizer use the the unwanted index and I want to force it use another plan.
So I generate the good plan using index hint and then created the baseline with the plans
and connect the wanted plan to the query sql_id, and change the fixed attribute to 'YES'.
I executed the DBMS_XPLAN.DISPLAY_SQL_PLAN_BASELINE function
and the output shows that the wanted plan marked as fixed=yes.
So why when I'm running the query it still with the bad plan??
The code:
-- Query
SELECT DISTINCT t_01.puid
FROM PWORKSPACEOBJECT t_01 , PPOM_APPLICATION_OBJECT t_02
WHERE ( ( UPPER(t_01.pobject_type) IN ( UPPER( :1 ) , UPPER( :2 ) )
AND ( t_02.pcreation_date >= :3 ) ) AND ( t_01.puid = t_02.puid ) )
-- get the text
select sql_fulltext
from v$sqlarea
where sql_id = '21pts328r2nb7' and rownum = 1;
-- prepare the explain plan
explain plan for
SELECT DISTINCT t_01.puid
FROM PWORKSPACEOBJECT t_01 , PPOM_APPLICATION_OBJECT t_02
WHERE ( ( UPPER(t_01.pobject_type) IN ( UPPER( :1 ) , UPPER( :2 ) )
AND ( t_02.pcreation_date >= :3 ) ) AND ( t_01.puid = t_02.puid ) ) ;
-- we can see that there is no use of index - PIPIPWORKSPACEO_2
select * from table(dbms_xplan.display);
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10382 | 517K| 61553 |
| 1 | HASH UNIQUE | | 10382 | 517K| 61553 |
| 2 | HASH JOIN | | 158K| 7885K| 61549 |
| 3 | INLIST ITERATOR | | | | |
| 4 | TABLE ACCESS BY INDEX ROWID| PWORKSPACEOBJECT | 158K| 4329K| 52689 |
| 5 | INDEX RANGE SCAN | PIPIPWORKSPACEO_3 | 158K| | 534 |
| 6 | INDEX RANGE SCAN | DBTAO_IX1_PPOM | 3402K| 74M| 2911 |
------------------------------------------------------------------------------------
Note
-----
- 'PLAN_TABLE' is old version
-- generate plan with the wanted index
explain plan for
select /*+ index(t_01 PIPIPWORKSPACEO_2)*/ distinct t_01.puid
from pworkspaceobject t_01 , ppom_application_object t_02
where ( ( upper(t_01.pobject_type) in ( upper( :1 ) , upper( :2 ) )
and ( t_02.pcreation_date >= :3 ) ) and ( t_01.puid = t_02.puid ) ) ;
-- the index working - the index used
select * from table(dbms_xplan.display);
-----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10382 | 517K| 223K|
| 1 | HASH UNIQUE | | 10382 | 517K| 223K|
| 2 | HASH JOIN | | 158K| 7885K| 223K|
| 3 | TABLE ACCESS BY INDEX ROWID| PWORKSPACEOBJECT | 158K| 4329K| 214K|
| 4 | INDEX FULL SCAN | PIPIPWORKSPACEO_2 | 158K| | 162K|
| 5 | INDEX RANGE SCAN | DBTAO_IX1_PPOM | 3402K| 74M| 2911 |
-----------------------------------------------------------------------------------
Note
-----
- 'PLAN_TABLE' is old version
-- get the sql_id of the query with the good index
-- 7t72qvghr0zqh
select sql_id from v$sqlarea where sql_text like 'select /*+ index(t_01 PIPIPWORKSPACEO_2)%';
-- get the plan hash value of the good plan by the sql_id
--4040955653
select plan_hash_value from v$sql_plan where sql_id = '7t72qvghr0zqh';
-- get the plan hash value of the bad plan by the sql_id
--1044780890
select plan_hash_value from v$sql_plan where sql_id = '21pts328r2nb7';
-- load the source plan
begin
dbms_output.put_line(
dbms_spm.load_plans_from_cursor_cache
( sql_id => '21pts328r2nb7' )
);
END;
-- the new base line created with the bad plan
select * from dba_sql_plan_baselines;
-- load the good plan of the second sql_id (with the wanted index)
-- and bind it to the sql_handle of the source query
begin
dbms_output.put_line(
DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE
( sql_id => '7t72qvghr0zqh',
plan_hash_value => 4040955653,
sql_handle => 'SQL_4afac4211aa3317d' )
);
end;
-- new there are 2 plans bind to the same sql_handle and sql_text
select * from dba_sql_plan_baselines;
-- alter the good one to be fixed
begin
dbms_output.put_line(
dbms_spm.alter_sql_plan_baseline
( sql_handle =>
'SQL_4afac4211aa3317d',
PLAN_NAME => 'SQL_PLAN_4pyq444da6cbxf7c97cc7',
ATTRIBUTE_NAME => 'fixed',
ATTRIBUTE_VALUE => 'YES'
)) ;
end;
-- check the good plan - fixed = yes
select * from table(
dbms_xplan.display_sql_plan_baseline (
sql_handle => 'SQL_4afac4211aa3317d',
plan_name => 'SQL_PLAN_4pyq444da6cbxf7c97cc7',
format => 'ALL'));
--------------------------------------------------------------------------------
SQL handle: SQL_4afac4211aa3317d
SQL text: SELECT DISTINCT t_01.puid FROM PWORKSPACEOBJECT t_01 ,
PPOM_APPLICATION_OBJECT t_02 WHERE ( ( UPPER(t_01.pobject_type) IN (
UPPER( :1 ) , UPPER( :2 ) ) AND ( t_02.pcreation_date >= :3 ) ) AND (
t_01.puid = t_02.puid ) )
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Plan name: SQL_PLAN_4pyq444da6cbxf7c97cc7 Plan id: 4157177031
Enabled: YES Fixed: YES Accepted: YES Origin: MANUAL-LOAD
--------------------------------------------------------------------------------
Plan hash value: 4040955653
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10382 | 517K| | 223K (1)| 00:44:37 |
| 1 | HASH UNIQUE | | 10382 | 517K| | 223K (1)| 00:44:37 |
|* 2 | HASH JOIN | | 158K| 7885K| 6192K| 223K (1)| 00:44:37 |
| 3 | TABLE ACCESS BY INDEX ROWID| PWORKSPACEOBJECT | 158K| 4329K| | 214K (1)| 00:42:50 |
|* 4 | INDEX FULL SCAN | PIPIPWORKSPACEO_2 | 158K| | | 162K (1)| 00:32:25 |
|* 5 | INDEX RANGE SCAN | DBTAO_IX1_PPOM | 3402K| 74M| | 2911 (1)| 00:00:35 |
-----------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$1
3 - SEL$1 / T_01#SEL$1
4 - SEL$1 / T_01#SEL$1
5 - SEL$1 / T_02#SEL$1
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("T_01"."PUID"="T_02"."PUID")
4 - filter(UPPER("POBJECT_TYPE")=UPPER(:1) OR UPPER("POBJECT_TYPE")=UPPER(:2))
5 - access("T_02"."PCREATION_DATE">=:3)
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - (#keys=1) "T_01"."PUID"[VARCHAR2,15]
2 - (#keys=1) "T_01"."PUID"[VARCHAR2,15]
3 - "T_01"."PUID"[VARCHAR2,15]
4 - "T_01".ROWID[ROWID,10]
5 - "T_02"."PUID"[VARCHAR2,15]
Note
-----
- 'PLAN_TABLE' is old version
-- run explain plan for the query
-- need to use the new plan
declare
v_string clob;
begin
select sql_fulltext
into v_string
from v$sqlarea
where sql_id = '21pts328r2nb7' and rownum = 1;
execute immediate 'explain plan for ' || v_string using '1','1',sysdate;
end;
-- check the plan - still the unwanted index and plan
select * from table(dbms_xplan.display);
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10382 | 517K| 61553 |
| 1 | HASH UNIQUE | | 10382 | 517K| 61553 |
| 2 | HASH JOIN | | 158K| 7885K| 61549 |
| 3 | INLIST ITERATOR | | | | |
| 4 | TABLE ACCESS BY INDEX ROWID| PWORKSPACEOBJECT | 158K| 4329K| 52689 |
| 5 | INDEX RANGE SCAN | PIPIPWORKSPACEO_3 | 158K| | 534 |
| 6 | INDEX RANGE SCAN | DBTAO_IX1_PPOM | 3402K| 74M| 2911 |
------------------------------------------------------------------------------------
Note
-----
- 'PLAN_TABLE' is old version
From a read through of your test case, I suspect the problem is that you're interpreting the FIXED attribute incorrectly.
If you list all the plans for your baseline, you will probably find the original and the loaded cursor plan are both ENABLED and ACCEPTED at the moment. I think what you need to do (based on my own usage of these calls) is use the ENABLED attribute. Set ENABLED to NO for the unwanted plan.
Try:
exec dbms_spm.alter_sql_plan_baseline(
sql_handle=>'SQL_...' -- baseline to update
,plan_name=>'SQL_PLAN_...' -- unwanted plan signature to disable
,attribute_name=>'ENABLED',attribute_value=>'NO')

Update between tables

I have two tables (ORACLE11) :
TABLE1: (ID_T1 is TABLE1 primary key)
| ID_T1 | NAME | DATEBEGIN | DATEEND |
| 10 | test | 01/01/2017 | 01/06/2017 |
| 11 | test | 01/01/2017 | null |
| 12 | test1 | 01/01/2017 | 01/06/2017 |
| 13 | test1 | 01/01/2017 | null |
TABLE2: (ID_T2 is TABLE2 primary key and ID_T1 is TABLE2 foreign key on TABLE1)
| ID_T2 | ID_T1 |
| 1 | 10 |
| 2 | 11 |
| 3 | 11 |
| 4 | 12 |
| 5 | 13 |
I need to delete all rows from TABLE1 where TABLE1.DATEEND = 'null'
But first I must update TABLE2 to modify TABLE2.ID_T1 to the remaining record in TABLE1 for the same NAME :
TABLE2:
| ID_T2 | ID_T1 |
| 1 | 10 |
| 2 | 10 |
| 3 | 10 |
| 4 | 12 |
| 5 | 12 |
I tried this:
UPDATE TABLE2
SET TABLE2.ID_T1 = (
SELECT TABLE1.ID_T1
FROM TABLE1
WHERE TABLE1.DATEBEGIN = '01/01/2017'
AND TABLE1.DATEEND IS NOT NULL
)
WHERE TABLE2.ID_T1 = (
SELECT TABLE1.ID_T1
FROM TABLE1
WHERE TABLE1.DATEBEGIN = '01/01/2017'
AND TABLE1.DATEEND IS NULL
);
But I don't know how to join on TABLE1.NAME and do it for all rows of TABLE2. Thanks in advance for your help.
First create a temp table to find out which id is to be retained for which name. In the case od multiple possible values, I have selected one by ascending order or id_t1.
create table table_2_update as
select id_t1, name from (select id_t1, name row_number() over(partition by
name order by id_t1) from table1 where name is not null) where rn=1;
Create next table to know which id of table2 connects to which name of table1.
create table which_to_what as
select t2.id_t2, t2.id_t1, t1.name from table1 t1 inner join table2 t2 on
t1.id_t1 = t2.id_t2 group by t2.id_t2, t2.id_t1, t1.name;
Since this newly created table now contains id and name of table1, and id of table2, merge into it to retain one to one case of id and name of table1.
merge into which_to_what a
using table_2_update b
on (a.name=b.name)
when matched then update set
a.id_t1=b.id_t1;
Now finally we have a table which contains the final correct values, you can either rename this tale to table2 or merge original table2 on the basis of id of new table and original table2.
merge into table2 a
using which_to_what a
on (a.id_t2=b.id_t2)
when matched then update set
a.id_t1=b.id_t1;
Finally delete null values from table1.
delete from table1 where dateend is null;
You can do this by joining table1 to itself, joining on the name column. Use the first table (a) to link to table2.id_t1 and the second table (b) to get the t1_id where dateend is not null.
UPDATE table2
SET table2.id_t1 = (
select b.id_t1
from table1 a, table1 b
where a.name = b.name
and b.dateend is not null
and a.id_t1 = table2.id_t1
)
WHERE EXISTS (
select b.id_t1
from table1 a, table1 b
where a.name = b.name
and b.dateend is not null
and a.id_t1 = table2.id_t1
);
This assumes that there will only be one table1 record where dateend is not null.

Unable to extend temp segment by 128 in tablespace TEMP. Another option to execute that query?

I am new in Oracle SQL and I am trying to make an update of a table with the next context:
I have a table A:
+---------+---------+---------+----------+
| ColumnA | name | ColumnC | Column H |
+---------+---------+---------+----------+
| 1 | Harry | null | null |
| 2 | Harry | null | null |
| 3 | Harry | null | null |
+---------+---------+---------+----------+
And a table B:
+---------+---------+---------+
| name | ColumnE | ColumnF |
+---------+---------+---------+
| Harry | a | d |
| Ron | b | e |
| Hermione| c | f |
+---------+---------+---------+
And I want to update the table A so that the result will be the next:
+---------+---------+---------+----------+
| ColumnA | name | ColumnC | Column H |
+---------+---------+---------+----------+
| 1 | Harry | a | d |
| 2 | Harry | a | d |
| 3 | Harry | a | d |
+---------+---------+---------+----------+
I have an issue with an Oracle SQL sentence. I have the next context:
merge into tableA a
using tableB b
on (a.name=b.name)
when matched then update set
columnC = b.columnE,
columnH = b.columnF
create table tableA (columnC varchar2(20), columnH varchar2(20), name varchar2(20), columnA number);
create table tableB (columnE varchar2(20), columnF varchar2(20), name varchar2(20));
insert into tableA values (null, null,'Harry',1);
insert into tableA values (null, null,'Harry',3);
insert into tableA values (null, null,'Harry',3);
insert into tableB values ('a', 'd','Harry');
insert into tableB values ('b', 'e','Ron');
insert into tableB values ('c', 'f','Hermione');
select * from tableA;
merge into tableA a
using tableB b
on (a.name=b.name)
when matched then update set
columnC = b.columnE,
columnH = b.columnF;
select * from tableA;
The problem is that I get the next error when I execute that command:
Error: ORA-01652: unable to extend temp segment by 128 in tablespace
TEMP
I cannot give more space to TEMP tablespace. So, my question is: Is there any option to use another SQL query that doesn't use TEMP tablespace?
you can try the following query maybe it will consume less TEMP tablespace:
update tableA
set (columnC, columnH ) = (select ColumnE, ColumnF from tableB where tableB.name = tableA.name)
where
tableA.name in (select tableB.name from tableB)
;
Or you can try to perform an update in small chunks in a loop. It's less perfomant, but if you have no other way ...
begin
FOR rec in
(select name, ColumnE, ColumnF from tableB)
LOOP
update tableA
set
columnC = rec.columnE
, columnH = rec.columnF
where name = rec.name
;
end loop;
end;
/

Resources