NOTE: This text contains a lot of grammar errors, so read it only if you really want to know something about views in the Oracle database. = Views == What are views Views are objects in a database, which: 1. Look like a table 2. Contain inside themselves a SQL query, by which a view is replaced when it's used. Views work like tables in many ways. We can (with some restrictions) insert, update and delete data from them. == Creating views General syntax is: ``` create view viewname as select <columns> from <table>; ``` It means that a SQL query with needed data is enough for view creation. We can create views with `or replace` option, and then, if the view with such name already exists, it will be overwritten: ``` create or replace view viewname as select <columns> from <table>; ``` Let's create tables for employees, positions and departments: ``` create table employees( id number, emp_name varchar2(100 char), dept_id number, position_id number ); create table departments( id number, dept_name varchar2(100) ); create table positions( id number, position_name varchar2(100) ); insert into departments values(1, 'IT'); insert into departments values(2, 'SALARY'); insert into positions values(1, 'MANAGER'); insert into positions values(2, 'CLERK'); insert into employees values(1, 'Ivan Boika', 1, 1); insert into employees values(2, 'John Rosenshteiner', 1, 2); insert into employees values(3, 'Philipp Dauhn', 2, 1); insert into employees values(4, 'Sid Vicious', 2, 2); ``` Now, let's create a `vemployees` view, which will contain data about employees from all these tables in a "joined" state: ``` create view vemployees as select e.id, e.emp_name, d.dept_name, p.position_name from employees e join departments d on d.id = e.dept_id join positions p on p.id = e.position_id; comment on table vemployees is 'employees'; comment on column vemployees.id is 'employee id'; comment on column vemployees.emp_name is 'employee name'; comment on column vemployees.dept_name is 'department'; comment on column vemployees.position_name is 'position'; ``` Note that views and their columns may contain comments like ordinary tables in Oracle. Now, to get interested data, we don't have to write a SQL query again, it's enough to get it from the view: ``` select * from vemployees ``` ``` +----+--------------------+-----------+---------------+ | ID | EMP_NAME | DEPT_NAME | POSITION_NAME | +----+--------------------+-----------+---------------+ | 1 | Ivan Boika | IT | MANAGER | | 3 | Philipp Dauhn | SALARY | MANAGER | | 2 | John Rosenshteiner | IT | CLERK | | 4 | Sid Vicious | SALARY | CLERK | +----+--------------------+-----------+---------------+ ``` We can use existing views to create other views: ``` create view vemployees_it as select a.* from vemployees a where a.dept_name = 'IT'; ``` We should carefully use existing views as build blocks for new ones. It may turn out that it's better to write completely new SQL query which is more optimal for specific task. == * Symbol When Oracle meets `*` in a view's definition, it replaces an asterics with the list of columns returned by the query. It means that if a new column is added to some table, Oracle won't add it to the view. It's pretty easy to check: ``` create table tst( n1 number, n2 number ); insert into tst values(1, 2); create view v_tst as select * from tst; ``` Let's see what data is in the view: ``` select * from v_tst ``` ``` +----+----+ | N1 | N2 | +----+----+ | 1 | 2 | +----+----+ ``` Now let's add a column to the `tst` table: ``` alter table tst add (n3 number); ``` If we look at the view, we will see that the column list hasn't been changed: ``` +----+----+ | N1 | N2 | +----+----+ | 1 | 2 | +----+----+ ``` To add the `n3` column to the view, we can change it by adding this column to the columns list, or recreate the view again (with `create or replace`): ``` create or replace view v_tst as select * from tst ``` == Modify view data Tables that are used in a view are called "base tables". Views that consist from one base table can be changed like an ordinary table. For example, let's create a `vdepartments` view and add some rows to it: ``` -- Create a view create view vdepartments as select id, dept_name from departments; -- Insert row through the view, not through a table insert into vdepartments(id, dept_name) values(10, 'SALES'); ``` Of course, a row is inserted into a base table ( the `department` table in this case). We can also delete and update rows in a base table: ``` delete from vdepartments where id = 10; update vdepartments set dept_name = 'SECURITY' where id = 1; ``` Let's look at the view: ``` select * from vdepartments ``` Result: ``` +----+-----------+ | ID | DEPT_NAME | +----+-----------+ | 1 | SECURITY | | 2 | SALARY | +----+-----------+ ``` == Views with check option We can create a view which will restrict data changing in base tables. `WITH CHECK OPTION` is used for this during view creation. Let's create a view with managers only: ``` create view vemp_managers as select * from employees where position_id = 1 ``` This view contains only managers, but it doesn't mean that we can't insert into employees with a different position into it: ``` -- Add an employee with position_id = 2 insert into vemp_managers(id, emp_name, dept_id, position_id) values(10, 'John Doe', 1, 2); ``` After that, the view looks the same: ``` select * from vemp_managers ``` ``` +----+---------------+---------+-------------+ | ID | EMP_NAME | DEPT_ID | POSITION_ID | +----+---------------+---------+-------------+ | 1 | Ivan Boika | 1 | 1 | | 3 | Philipp Dauhn | 2 | 1 | +----+---------------+---------+-------------+ ``` But the `employees` table itself now has new rows: ``` select * from employees ``` ``` +----+--------------------+---------+-------------+ | ID | EMP_NAME | DEPT_ID | POSITION_ID | +----+--------------------+---------+-------------+ | 10 | John Doe | 1 | 2 | | 1 | Ivan Boika | 1 | 1 | | 2 | John Rosenshteiner | 1 | 2 | | 3 | Philipp Dauhn | 2 | 1 | | 4 | Sid Vicious | 2 | 2 | +----+--------------------+---------+-------------+ ``` In order to be able to modify only rows that a view has, `with check option` option is used. Let's try it: ``` create or replace view vemp_managers as select * from employees where position_id = 1 with check option; insert into vemp_managers(id, emp_name, dept_id, position_id) values(11, 'John Doe', 1, 2); ``` We'll get the `view WITH CHECK OPTION where-clause violation` exception if we run example above. == Modify views that are made up of multiple tables In Oracle, we can change data via views that are made up of multiple tables, but with some restrictions: 1. It's possible to change only one base table 2. Affected table should be a "key preserved table". The last point is the most important for understanding whether data can be changed in a multi-table view or not. A table is key preserved, if each of its rows appears at most once in a view. It should be remembered that a key preserved property doesn't depend on data, but rather on tables' structure and relations between them. In fact, data in a view might look like that for one row in a base table there's one row in the view. But it doesn't mean that this property will not be changed after data modifications in tables inside the view. For demonstration, we're going to create a `vemp_depts` view, which will contain information about employees and their departments: ``` create or replace view vemp_depts as select e.id, e.emp_name, e.dept_id, e.position_id, d.id department_id, d.dept_name from employees e join departments d on e.dept_id = d.id ``` Let's see what's inside this view: ``` select * from vemp_depts ``` ``` +----+--------------------+---------+-------------+---------------+-----------+ | ID | EMP_NAME | DEPT_ID | POSITION_ID | DEPARTMENT_ID | DEPT_NAME | +----+--------------------+---------+-------------+---------------+-----------+ | 10 | John Doe | 1 | 2 | 1 | SECURITY | | 1 | Ivan Boika | 1 | 1 | 1 | SECURITY | | 2 | John Rosenshteiner | 1 | 2 | 1 | SECURITY | | 3 | Philipp Dauhn | 2 | 1 | 2 | SALARY | | 4 | Sid Vicious | 2 | 2 | 2 | SALARY | +----+--------------------+---------+-------------+---------------+-----------+ ``` As we can see, each row from the `employees` base table apppears only once in the `vemp_depts` view. Now, let's try to add new employee through this view: ``` insert into vemp_depts(id, emp_name, dept_id, position_id) values(20, 'Johny Belaco', 1, 1); ``` As a result, we're getting an "cannot modify a column which maps to a non key-preserved" error, which tells us that the table doesn't meet the requirements for updating it through the view. We know that the problem is not with data, but with database structure. Let's see how we've created our table and how our query looks inside the view: ``` -- This is what is inside the view select e.id, e.emp_name, e.dept_id, e.position_id, d.id department_id, d.dept_name from employees e join departments d on e.dept_id = d.id ``` Here, we get each row from the `employees` table and join it with the `departments` table by the `dept_id` field. In which case it may be happened that inside the view there will be two records after joining one single row from the `employees` table with the `departments` table? Right, when there are two rows in the `departments` table with the same value in the `id` field. Now there are no such rows, but it doesn't mean that they will not appear. Now, we have to remember how we have created the `department` table: ``` create table departments( id number, dept_name varchar2(100) ) ``` There are no any restrictions for the `id` column. But we can make it unique by adding a primary or unique key: ``` alter table departments add ( constraint departments_pk primary key(id) ) ``` Now, if we try to add a new employee, everything will be fine: ``` -- A row will be added without errors insert into vemp_depts(id, emp_name, dept_id, position_id) values(20, 'Random Employee', 1, 1); ``` It should me mentioned that it's impossible to insert rows into the `departments` table via this view: ``` -- cannot modify a column which maps to a non key-preserved table insert into vemp_depts(department_id, dept_name) values(7, 'HEAD DEPARTMENT') ``` The reason is still the same - it's not guaranteed that each employee in the `employees` table has a unique value of the `dept_id` field. === Restrictions on changing data via views There're some cases when changing base tables through a view is not possible: 1. If view has aggregate functions, `group by` constructions, `distinct` operator, as well as union operators (`union`, `union all`, `intersect`) 2. If data isn't satisfy conditions in `WITH CHECK OPTION` clause 3. If some column in a base table is `NOT NULL`, doesn't have default value and doesn't exist in the view 4. If columns in a view are expressions (something like `nvl(a.value, -1)`) == Prohibition of data modification through views To create a read-only view, we can use the `with read only` modificator: ``` -- Create a read only view create or replace view vdepartments as select id, dept_name from departments with read only; -- let's try to insert a row insert into vdepartments(id, dept_name) values(11, 'SECURITY'); ``` As a result, we get an `cannot perform a DML operation on a read-only view` error.