【问题标题】:How to update while joining the table加入表格时如何更新
【发布时间】:2020-01-05 13:57:31
【问题描述】:

我有三张桌子。

Stude_course 及其记录如下

empid   ename   emp_status  emp_year
1   Raja    6           1
2   Poo     5           2
3   Bhasker 6           3

Student1 表包含以下记录

empid   ename   emp_status
1   Raja        6
2   Poo         6
3   Bhasker     6

Stud_year 包含以下记录

empid   emp_year
1       1
2       1
3       1

我需要一个查询来使用 student1.emp_status 更新 stude_course.emp_status 并使用 stud_year.emp_year 更新 stude_course.emp_year。为了更新这些记录,我使用了下面的查询

update
(
select sc.emp_status stud_emp_status,sc.emp_year stud_emp_year,s.emp_status stud_status,sy.emp_year stud_year from stude_course sc,student1 s,stud_year sy
where sc.empid = s.empid
and s.empid = sy.empid
and sc.empid = 2) st
set st.stud_emp_status = st.stud_status, st.stud_emp_year = st.stud_year;

我已经使用 equi join 连接了三个表,并为整个连接的表指定了一个别名 st,还为列名指定了别名,然后我尝试使用给定的别名表和列名来更新值

预期结果:

empid   ename   emp_status  emp_year
1   Raja        6           1
2   Poo         6           1
3   Bhasker     6           1

But i got error like 

SQL Error: ORA-01779: cannot modify a column which maps to a non key-preserved table
01779. 00000 -  "cannot modify a column which maps to a non key-preserved table"
*Cause:    An attempt was made to insert or update columns of a join view which
           map to a non-key-preserved table.
*Action:   Modify the underlying base tables directly.

【问题讨论】:

    标签: sql oracle join sql-update


    【解决方案1】:

    可以直接更新表格如下:

    UPDATE STUDE_COURSE SC
    SET (SC.EMP_YEAR, SC.EMP_STATUS) = 
    (SELECT SY.EMP_YEAR, S1.STATUS FROM STUD_YEAR SY JOIN STUDENT1 S1 
    ON (S1.EMPID = SY.EMPID)
    WHERE SY.EMPID = SC.EMPID)
    WHERE SC.EMPID = 2;
    

    或者你也可以使用MERGE,如下:

    MERGE INTO STUDE_COURSE SC
    USING
    (SELECT SY.EMP_YEAR, S1.EMP_STATUS, S1.EMPID FROM STUD_YEAR SY JOIN STUDENT1 S1 
    ON (S1.EMPID = SY.EMPID)) S
    ON (SC.EMPID = S.EMPID)
    WHEN MATCHED THEN
    UPDATE SET SC.EMP_STATUS = S.EMP_STATUS, SC.EMP_YEAR = S.EMP_YEAR
    

    干杯!!

    【讨论】:

      【解决方案2】:

      我不确定您使用的更新子查询语法在涉及联接时是否可行。我们可以尝试使用相关子查询来编写更新:

      UPDATE stude_course sc
      SET
          emp_status = (SELECT s.emp_status
                        FROM student1 s
                        INNER JOIN stud_year sy ON s.empid = sy.empid
                        WHERE sc.empid = s.empid),
          emp_year = (SELECT sy.emp_year
                      FROM student1 s
                      INNER JOIN stud_year sy ON s.empid = sy.empid
                      WHERE sc.empid = s.empid)
      WHERE
          sc.empid = 2;
      

      【讨论】:

      • 这也可以用单个子查询来编写。 (emp_status, emp_year) = (select s.emp_status, sy.emp_yer from ...)
      • @a_horse_with_no_name 绝招...我会努力记住的!
      【解决方案3】:

      无法修改映射到非键保留表的列

      您尝试更新的列位于STUDE_COURSE 表中。但是 Oracle 在查看了您的表结构后,决定您的连接查询不能保证在 STUDE_COURSE 中的每一行只包含一次。

      如果您向表中添加约束以向 Oracle 保证 STUDE_COURSE 中的行不会在您的查询中重复,那么您的 UPDATE 将起作用。

      让我们来看看吧。

      首先,让我们重新创建您当前的情况:

      设置表格和数据

      CREATE TABLE stude_course 
        ( empid number not null,
          ename varchar2(30) not null,
          emp_status number,
          emp_year number );
      
      CREATE TABLE student1 
        ( empid number not null,
          ename varchar2(30) not null,
          emp_status number );
      
      CREATE TABLE stud_year
        ( empid number not null,
          emp_year number );
      
      INSERT INTO stude_course ( empid, ename, emp_status, emp_year)
        VALUES ( 1, 'Raja', 6, 1 );
      
      INSERT INTO stude_course ( empid, ename, emp_status, emp_year)
        VALUES ( 2, 'Poo', 5, 2 );
      
      INSERT INTO stude_course ( empid, ename, emp_status, emp_year)
        VALUES ( 3, 'Bhasker', 6, 3 );  
      
      
      INSERT INTO student1 ( empid, ename, emp_status)
        VALUES (1, 'Raja', 6);
      
      INSERT INTO student1 ( empid, ename, emp_status)
        VALUES (2, 'Poo', 6);
      
      INSERT INTO student1 ( empid, ename, emp_status)
        VALUES (3, 'Bhasker', 6);
      
      INSERT INTO stud_year ( empid, emp_year)
        VALUES (1, 1);
      
      INSERT INTO stud_year ( empid, emp_year)
        VALUES (2, 1);  
      
      INSERT INTO stud_year ( empid, emp_year)
        VALUES (3, 1);    
      
      COMMIT;  
      

      尝试UPDATE(失败)

      update
      (
      select sc.emp_status stud_emp_status,
             sc.emp_year stud_emp_year,
             s.emp_status stud_status,
             sy.emp_year stud_year 
      from stude_course sc INNER JOIN student1 s ON s.empid=sc.empid
      INNER JOIN stud_year sy ON sy.empid=s.empid
      where sc.empid = 2) st
      set st.stud_emp_status = st.stud_status, st.stud_emp_year = st.stud_year;
      
      SQL Error: ORA-01779: cannot modify a column which maps to a non key-preserved table
      01779. 00000 -  "cannot modify a column which maps to a non key-preserved table"
      *Cause:    An attempt was made to insert or update columns of a join view which
                 map to a non-key-preserved table.
      *Action:   Modify the underlying base tables directly.
      

      添加约束告诉 Oracle 连接不会复制目标表中的行

      alter table stude_course add constraint stud_course_pk PRIMARY KEY ( empid );
      
      alter table student1 add constraint student1_pk PRIMARY KEY ( empid );
      
      alter table stud_year add constraint stud_year_pk PRIMARY KEY ( empid );
      

      再试一次...(有效)

      update
      (
      select sc.emp_status stud_emp_status,
             sc.emp_year stud_emp_year,
             s.emp_status stud_status,
             sy.emp_year stud_year 
      from stude_course sc INNER JOIN student1 s ON s.empid=sc.empid
      INNER JOIN stud_year sy ON sy.empid=s.empid
      where sc.empid = 2) st
      set st.stud_emp_status = st.stud_status, st.stud_emp_year = st.stud_year;
      

      更新了 1 行。

      性能警告

      这种语法并不常见(请参阅发布的其他答案以获取替代方案),我确实遇到过这种奇怪的情况。发生的情况是,CBO 优化了连接,结果集中的行顺序与实际更新的表中的行顺序不同。结果,Oracle 到处更新块,多次触及每个块,导致性能非常差。添加ORDER BY target_table.rowid 修复它。那真是令人头疼。

      【讨论】:

        猜你喜欢
        • 2010-10-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-22
        • 1970-01-01
        • 1970-01-01
        • 2018-07-03
        • 2021-02-28
        相关资源
        最近更新 更多