Skip to Main Content

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Imran.

Asked: February 25, 2016 - 1:11 pm UTC

Last updated: March 01, 2016 - 1:22 am UTC

Version: 11g

Viewed 50K+ times! This question is

You Asked

SELECT TEMP.unqid,TEMP.MD_DT
FROM (
SELECT t2.unqid AS unqid,t2.MD_DT AS MD_DT,
ROW_NUMBER() OVER (PARTITION BY t2.unqid ORDER BY t2.MD_DT DESC) AS ROW_ID
FROM t2, t1
WHERE t2.x = 'x'
AND t2.y = 'y'
AND t2.MD_DT IS NOT NULL
AND t1.unqid=t2.unqid
AND t1.lt='hl'
) TEMP
WHERE TEMP.ROW_ID=1
;

This query returns uniqueid, md_Dt for every valid uniqueid. this has to be updated back into t1.md_dt.

We tried using update with select but got the error 'cannot modify a column which maps to a non key-preserved table'.

Can you please suggest easy method of updating temp.md_dt into t1.md_dt.

and Chris said...

What exactly were you trying when you received the error?

You could do something like this:

update t1 
set    md_dt = (
  select md_dt from (
    select t2.unqid as unqid,
           t2.md_dt as md_dt, 
           row_number ( ) over ( partition by t2.unqid order by t2.md_dt desc ) as row_id
    from   t2, t1
    where  t2.x    = 'x'
    and    t2.y    = 'y'
    and    t2.md_dt is not null
  ) tmp
  where row_id = 1
  and   tmp.unqid = t1.unqid
  and   t1.lt ='hl'
)
where t1.lt ='hl';


You may need to extend the update's where clause to check there exists a t2 row as well.

Rating

  (6 ratings)

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

Comments

alternate

A reader, February 25, 2016 - 5:08 pm UTC

I think we can use merge as well in this case.
Chris Saxon
February 25, 2016 - 5:44 pm UTC

Yep, there are a number of ways you could write the update.

A bit slower

Imran, February 26, 2016 - 6:22 am UTC

Hi,

We were trying the below query
select
t1.unqid,
t1,md_dt,
newt.MD_DT
from
t1,
(SELECT TEMP.unqid,TEMP.MD_DT
FROM (
SELECT t2.unqid AS unqid,t2.MD_DT AS MD_DT,
ROW_NUMBER() OVER (PARTITION BY t2.unqid ORDER BY t2.MD_DT DESC) AS ROW_ID
FROM t2, t1
WHERE t2.x = 'x'
AND t2.y = 'y'
AND t2.MD_DT IS NOT NULL
AND t1.unqid=t2.unqid
AND t1.lt='hl'
) TEMP
WHERE TEMP.ROW_ID=1) Newt
where
newt.unqid=t1.unqid;

The select query that I shared completes in 13 seconds.

But the update query(after including the existence in t2) takes 250 seconds.

Can we use any other approach.
Or split the task into 2 - select all the rows and then update in size of say 1000 each.
Connor McDonald
February 26, 2016 - 10:21 am UTC

You may be able to write it as a merge or an updatable query - see Rajesh's comments below.

To help us understand what's going on with performance we need to see an execution plan though. If you post one of these we can help further.

https://oracle-base.com/articles/9i/dbms_xplan

cannot modify a column which maps to a non key-preserved table

Rajeshwaran Jeyabal, February 26, 2016 - 6:33 am UTC

rajesh@ORA11G> create table emp as select * from scott.emp;

Table created.

rajesh@ORA11G> create table dept as select * from scott.dept;

Table created.

rajesh@ORA11G> update ( select ename,dname
  2  from emp e, dept d
  3  where e.deptno = d.deptno )
  4  set ename = dname;
set ename = dname
    *
ERROR at line 4:
ORA-01779: cannot modify a column which maps to a non key-preserved table


The error message tell us that "deptno" column in dept table is not unique. so each and every record from emp table could join with more than one record in Dept table.
let say deptno=10 is repeated twice in dept table with different department names and emp table has three employees working for deptno=10 then join between these two tables would produce six rows in the output, database has no clue about which department name should be updated here. Hence this error message.

if update is turned into a merge, then we don't see this error message.
rajesh@ORA11G> merge into emp using dept
  2  on (emp.deptno = dept.deptno)
  3  when matched then
  4     update set ename = dname;

14 rows merged.

rajesh@ORA11G> rollback ;

Rollback complete.

rajesh@ORA11G>

that doesn't mean merge are solution to this case, the moment we insert deptno=10 twice into the dept table, then merge would result into an error like this.
rajesh@ORA11G> insert into dept
  2  select *
  3  from dept
  4  where deptno = 10;

1 row created.

rajesh@ORA11G>
rajesh@ORA11G> merge into emp using dept
  2  on (emp.deptno = dept.deptno)
  3  when matched then
  4     update set ename = dname;
merge into emp using dept
           *
ERROR at line 1:
ORA-30926: unable to get a stable set of rows in the source tables


rajesh@ORA11G> rollback ;

Rollback complete.

rajesh@ORA11G>

the only difference here is UPDATE does this check before execution, while MERGE does this as part of statement execution.
The right way to avoid this error is define either a primary key or a unique index/constraint on dept(deptno). That way deptno is not duplicated in DEPT table and every record from EMP table matches at most zero or one record in DEPT table.

rajesh@ORA11G> alter table dept add constraint dept_pk
  2  primary key(deptno);

Table altered.

rajesh@ORA11G> update ( select ename,dname
  2  from emp e, dept d
  3  where e.deptno = d.deptno )
  4  set ename = dname;

14 rows updated.

rajesh@ORA11G> rollback ;

Rollback complete.

rajesh@ORA11G> merge into emp using dept
  2  on (emp.deptno = dept.deptno)
  3  when matched then
  4     update set ename = dname;

14 rows merged.

rajesh@ORA11G> rollback;

Rollback complete.

rajesh@ORA11G>
rajesh@ORA11G> alter table dept drop constraint dept_pk;

Table altered.

rajesh@ORA11G> create unique index dept_pk on dept(deptno);

Index created.

rajesh@ORA11G> update ( select ename,dname
  2  from emp e, dept d
  3  where e.deptno = d.deptno )
  4  set ename = dname;

14 rows updated.

rajesh@ORA11G> rollback ;

Rollback complete.

rajesh@ORA11G> merge into emp using dept
  2  on (emp.deptno = dept.deptno)
  3  when matched then
  4     update set ename = dname;

14 rows merged.

rajesh@ORA11G> rollback;

Rollback complete.

rajesh@ORA11G>

Constraint on intermediate table

Imran, February 26, 2016 - 11:43 am UTC

select
t1.unqid,
t1,md_dt,
newt.MD_DT
from
t1,
(SELECT TEMP.unqid,TEMP.MD_DT
FROM (
SELECT t2.unqid AS unqid,t2.MD_DT AS MD_DT,
ROW_NUMBER() OVER (PARTITION BY t2.unqid ORDER BY t2.MD_DT DESC) AS ROW_ID
FROM t2, t1
WHERE t2.x = 'x'
AND t2.y = 'y'
AND t2.MD_DT IS NOT NULL
AND t1.unqid=t2.unqid
AND t1.lt='hl'
) TEMP
WHERE TEMP.ROW_ID=1) Newt
where
newt.unqid=t1.unqid;

Can we have unique key/primary key constraints on Newt.
If this is possible then i guess update will work and not give a non key-preserved table error.

Please help.
Chris Saxon
February 26, 2016 - 4:27 pm UTC

No, you define the constraints on the base tables (t1, t2). If you want more help with this, please provide the DDL for your tables (create table, constraints + indexes) and an execution plan for the update.

Imran, February 29, 2016 - 8:33 am UTC

Please find the details(DDL & Execution Plan) asked as of below:

create table t1(uniqid varchar2(100) primary key, lt varchar2(100), MD_DT date);
-- no other constraints or indexes on this table

create table t2(id varchar2(100) primary key, uniqid varchar2(100), x varchar2(100),y varchar2(100), MD_DT date);
-- no other constraints or indexes on this table

Explain plan
Operation OBJECT_NAME CARDINALITY COST
UPDATE STATEMENT 9162 46282317

UPDATE t1

NESTED LOOPS

NESTED LOOPS 9162 14217

SORT 9163 5048

TABLE ACCESS t2 9163 5048

INDEX t1_pk 1 1

TABLE ACCESS t1 1 2

VIEW 1 5049

WINDOW 1 5049

FILTER

TABLE ACCESS t2 1 5048

To Imran

Rajeshwaran, Jeyabal, February 29, 2016 - 3:26 pm UTC

Thanks for the DDL's. But where is your update statement? i dont see your update dml else where in this page apart from Initial response by Chirs.

share your update statement that is causing error.
Chris Saxon
March 01, 2016 - 1:22 am UTC

You need to help us out here

Send us something we can run from top to bottom so we can see what it is you are trying to achieve.

Cheers,
Connor

More to Explore

Analytics

Analytic SQL got you confused? Check out Connor McDonald's complete video course.