NOTICE: Greenberg 12000.00 NOTICE: Faviet 9000.00 NOTICE: Chen 8200.00 1. Представление 1) можно рассматривать как сохраненный SQL запрос NOTICE: Sciarra 7700.00 NOTICE: Urman 7800.00 2) может использоваться для NOTICE: Popp 6900.00 1. упрощенного представления данных NOTICE: Raphaely 11000.00 CREATE VIEW seattle_employees AS NOTICE: Weiss 8000.00 SELECT e.* FROM NOTICE: Fripp 8200.00 employees e, departments d, locations l, countries c .... WHERE e.department_id=d.department_id AND mydb=> explain analyze SELECT * from l.location_id=d.location_id AND l.country_id=c.country_id AND c.country_name='United employees_5000 where f(last_name,salary); QUERY PLAN States of America' AND l.city='Seattle'; ----------------------------------------------------------------------Seq Scan on employees (cost=0.00..4.34 rows=14 SELECT * FROM seattle_employees WHERE width=76) (actual time=3.960..7.294 rows=44 loops=1) salary<5000; Filter: (f(last_name, salary) AND (salary < 2. создания API для работы с данными (абстракция 5000::numeric)) Total runtime: 7.600 ms от данных) ALTER TABLE employees ADD age NUMERIC (3,0); (3 rows) -- При этом структура seattle_employees не изменится, старые приложения могут продолжать работать с этим 2. Обновляемые представления По умолчанию представления в PostgreSQL не представлением обновляемые. Их можно сделать обновляемыми при помощи правил. 3. защиты данных mydb=> CREATE VIEW employees_5000 AS CREATE VIEW employees_5000 AS SELECT * FROM employees WHERE salary<5000; SELECT * FROM employees WHERE salary<5000; Представления и материализованные представления в СУБД PostgreSQL CREATE USER user99 ENCRYPTED PASSWORD 'aaaa'; GRANT SELECT ON employees_5000 TO user99; mydb=> SELECT * from employees; ERROR: permission denied for relation employees mydb=> SELECT * from employees_5000; .... Недостаток: при наличии прав на создание агрегатов представления могут раскрывать информацию. mydb=> GRANT CREATE ON DATABASE mydb TO user99; CREATE RULE e5000_ins AS ON INSERT TO employees_5000 DO INSTEAD INSERT INTO employees SELECT new.* where new.salary<5000; CREATE RULE e5000_del AS ON DELETE TO employees_5000 DO INSTEAD DELETE FROM employees where salary<5000 and employee_id=old.employee_id; CREATE RULE e5000_upd AS ON UPDATE TO employees_5000 DO INSTEAD UPDATE employees SET first_name=new.first_name, last_name=new.last_name,email=new.email, mydb=> SELECT * from employees; phone_number=new.phone_number, ERROR: permission denied for relation employees hire_date=new.hire_date,job_id=new.job_id,salary=new.sa mydb=> CREATE OR REPLACE FUNCTION lary, f(varchar,numeric) RETURNS bool AS 'BEGIN RAISE NOTICE ''% %'', $1, $2; RETURN commission_pct=new.commission_pct,manager_id=new.m anager_id, department_id=new.department_id true; END;' WHERE employee_id=old.employee_id AND LANGUAGE plpgsql COST 0.00001; new.salary<5000 and old.salary<5000; mydb=> SELECT * from employees_5000 where f(last_name,salary); GRANT UPDATE,DELETE on employees_5000 to NOTICE: King 24000.00 user99; NOTICE: Kochhar 17000.00 NOTICE: De Haan 17000.00 mydb=> INSERT INTO employees_5000 VALUES NOTICE: Hunold 9000.00 (111111,'a','b','c@d.ru','111-222','2009-04-03 18:55', NOTICE: Ernst 6000.00 'AC_ACCOUNT',5600, 0.4, 100,90); INSERT 0 0 mydb=> INSERT INTO employees_5000 VALUES (111111,'a','b','c@d.ru','111-222','2009-04-03 18:55', 'AC_ACCOUNT',4600, 0.4, 100,90); INSERT 0 1 CREATE RULE e5000_del AS ON DELETE TO employees_5000 DO INSTEAD DELETE FROM employees where salary<5000 and employee_id=old.employee_id; 3) Создаем необходимые индексы ALTER TABLE seattle_employees ADD PRIMARY KEY(employee_id); CREATE INDEX se_salary ON seattle_employees (salary); CREATE INDEX se_last_name ON seattle_employees (last_name); ... 4) Создаем функцию для обновления записи CREATE OR REPLACE FUNCTION refresh_seattle_employees (id INTEGER) RETURNS mydb=> DELETE FROM employees_5000 WHERE VOID SECURITY DEFINER employee_id = 111111; AS $$ DELETE 1 DELETE FROM seattle_employees WHERE employee_id=$1; mydb=> UPDATE employees_5000 SET first_name='TJJ' INSERT INTO seattle_employees SELECT * FROM WHERE first_name='TJ'; seattle_employees_v WHERE employee_id=$1; UPDATE 1 $$ LANGUAGE SQL; mydb=> UPDATE employees_5000 SET salary=10000 where first_name='TJJ'; UPDATE 0 5) Создаем триггеры CREATE OR REPLACE FUNCTION mv_se_ins () RETURNS TRIGGER AS $$ begin SELECT refresh_seattle_employees(new.id); RETURN null; end; $$ LANGUAGE PLPGSQL; 3. Материализованные представления Можно рассматривать как предварительно вычисленные ответы на запросы. Напрямую не поддерживаются PostgreSQL. Ничто не мешает их реализовать на уровне приложения (например, с помощью триггеров). Виды мат. представлений: CREATE TRIGGER mv_se_employees_ins AFTER 1) snapshot (CREATE AS SELECT, обновление — по INSERT ON employees FOR EACH ROW EXECUTE расписанию) PROCEDURE mv_se_ins(); 2) very lazy (подобно snapshot, но при обновлении изменяются только строки, которые нужно обновить, CREATE OR REPLACE FUNCTION mv_se_upd () список строк на обновление поддерживается отдельно) RETURNS TRIGGER 3) lazy (обновление строк, которые нужно обновить, в AS $$ конце транзакции) begin 4) eager (подобно lazy, но обновляются на каждую if old.employee_id=new.employee_id then операцию изменения) SELECT refresh_seattle_employees(new.id); else 3.1 Пример создания eager materialized view. SELECT refresh_seattle_employees(old.id); 1) Создаем представление, на основе которого будем SELECT refresh_seattle_employees(new.id); делать мат. представление (должно включать end if; первичный ключ) RETURN null; CREATE VIEW seattle_employees_v AS end; SELECT e.* FROM $$ LANGUAGE PLPGSQL; employees e, departments d, locations l, countries c WHERE e.department_id=d.department_id AND CREATE TRIGGER mv_se_employees_ins AFTER l.location_id=d.location_id AND UPDATE ON employees FOR EACH ROW EXECUTE l.country_id=c.country_id AND c.country_name='United PROCEDURE mv_se_upd(); States of America' AND l.city='Seattle'; CREATE OR REPLACE FUNCTION mv_se_del () 2) Создаем начальный снимок RETURNS TRIGGER CREATE TABLE seattle_employees AS AS $$ SELECT * from seattle_employees_v; begin SELECT refresh_seattle_employees(old.id); RETURN null; end; $$ LANGUAGE PLPGSQL; CREATE TRIGGER mv_se_employees_del AFTER UPDATE ON employees FOR EACH ROW EXECUTE PROCEDURE mv_se_del(); -- Далее необходимо создать аналогичные триггеры для departments, locations, countries. Необходимо обрабатывать только те случаи, которые влияют на наше представление