添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

所谓联接视图,就是有多张基表的视图(From子句中引用了多张表或者视图)

通常情况下,只有满足了一定的条件,我们才能更新联接视图,比如

1、视图定义中没有 WITH READ ONLY选项

2、一次只能更新一张基表

3、只能更新映射到键值保存表(key-preserved table)的字段

4、满足其它简单视图的更新条件(比如没有聚合等)

什么是键值保存表?

如果基表的每条记录在视图结果中最多出现1次(或者不出现,即不会重复),那么该基表就是键值保存表。

例如:

CREATE VIEW staff_dept_10_30 AS
SELECT e.employee_id, e.last_name, e.job_id, d.department_id, d.department_name
FROM   employees e, departments d
WHERE  e.department_id IN (10, 30)
AND    e.department_id = d.department_id;

在该视图定义中,对于empoyees中的每条记录,在最终的视图结果中最多只能出现一次(department_id不为10、30的则不会出现);

而departments表中的记录则会重复出现多次,所以对于此视图,employees表为键值保存表,而departments表则不是

注:department_id为departments的主键

所以staff_dept_10_30视图中的employee_id,last_name,job_id可以更新(employees表是键值保存表),而department_id则不可更新(departments表不是键值保存表)

我们可以通过以下SQL来进行验证:

SELECT TABLE_NAME, COLUMN_NAME, UPDATABLE 
    FROM   USER_UPDATABLE_COLUMNS 
   WHERE  TABLE_NAME = 'STAFF_DEPT_10_30';
TABLE_NAME                     COLUMN_NAME                    UPD
------------------------------ ------------------------------ ---
STAFF_DEPT_10_30               EMPLOYEE_ID                    YES
STAFF_DEPT_10_30               LAST_NAME                      YES
STAFF_DEPT_10_30               JOB_ID                         YES
STAFF_DEPT_10_30               DEPARTMENT_ID                  NO
STAFF_DEPT_10_30               DEPARTMENT_NAME                NO

OK,让我们来验证一下

只更新employees的字段

SQL> update STAFF_DEPT_10_30 set last_name=last_name||'a';
7 rows updated

只更新departments的字段

SQL> update STAFF_DEPT_10_30 set DEPARTMENT_NAME=DEPARTMENT_NAME||'b';
update STAFF_DEPT_10_30 set DEPARTMENT_NAME=DEPARTMENT_NAME||'b'
ORA-01779: 无法修改与非键值保存表对应的列

那么Oracle怎么知道employees是键值保存表,而departments不是呢?

很简单,前面提到department_id为departments的主键;当表A通过另表B的主键来join表B里,表A的记录肯定不会重复!但表B的记录则不一定,所以此时表A是键值保存表,

而表B则不是。

那这样一来是不是我们就可以在表A的join字段上也建上主键(或者唯一索引)就也能让表B变成键值保存表呢?答案是肯定的

例如:

创建EMP表,让其每个DEPARTMENT_ID只有一条记录

create table emp
select *
  from (select a.*,
               row_number() over(partition by DEPARTMENT_ID order by EMPLOYEE_ID) lvl
          from employees a)
 where lvl = 1

在DEPARTMENT_ID上创建唯一索引

-- Create/Recreate indexes 
create unique index EMP_UK on EMP (department_id);

修改STAFF_DEPT_10_30的定义,让其引用EMP表

CREATE OR REPLACE VIEW staff_dept_10_30 AS
SELECT e.employee_id, e.last_name, e.job_id, d.department_id, d.department_name
FROM   emp e, departments d
WHERE  e.department_id IN (10, 30)
AND    e.department_id = d.department_id;

重新查询

SELECT TABLE_NAME, COLUMN_NAME, UPDATABLE 
    FROM   USER_UPDATABLE_COLUMNS 
   WHERE  TABLE_NAME = 'STAFF_DEPT_10_30';
TABLE_NAME                     COLUMN_NAME                    UPD
------------------------------ ------------------------------ ---
STAFF_DEPT_10_30               EMPLOYEE_ID                    YES
STAFF_DEPT_10_30               LAST_NAME                      YES
STAFF_DEPT_10_30               JOB_ID                         YES
STAFF_DEPT_10_30               DEPARTMENT_ID                  YES
STAFF_DEPT_10_30               DEPARTMENT_NAME                YES

OK,测试通过!

到现在为止,我们都遵循了前面提到的一般原则,则一次只更新一张基表以及只能更新映射到键值保存表(key-preserved table)的字段,

但是如果我们想要同时更新staff_dept_10_30的last_name和department_name应该怎么办呢?

答案是使用instead of类型的trigger

将STAFF_DEPT_10_30的定义还原

CREATE OR REPLACE VIEW staff_dept_10_30 AS
SELECT e.employee_id, e.last_name, e.job_id, d.department_id, d.department_name
FROM   employees e, departments d
WHERE  e.department_id IN (10, 30)
AND    e.department_id = d.department_id;

在视图上创建TRIGGER

CREATE OR REPLACE TRIGGER STAFF_DEPT_10_30_TRI
  INSTEAD OF UPDATE ON STAFF_DEPT_10_30
  FOR EACH ROW
DECLARE
BEGIN
  UPDATE EMPLOYEES
     SET LAST_NAME   = :NEW.LAST_NAME,
         JOB_ID      = :NEW.JOB_ID,
         DEPARTMENT_ID =:NEW.DEPARTMENT_ID
   WHERE employee_id = :OLD.employee_id;
  UPDATE DEPARTMENTS
     SET DEPARTMENT_NAME = :NEW.DEPARTMENT_NAME
   WHERE DEPARTMENT_ID = :OLD.DEPARTMENT_ID;
END STAFF_DEPT_10_30_TRI;

OK,测试一下!

SQL> update STAFF_DEPT_10_30
  2     set last_name = 'a', DEPARTMENT_NAME = DEPARTMENT_NAME || 'b'
  3   where employee_id = 200
1 row updated

测试通过!