материализованные представления в СУБД PostgreSQL

advertisement
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. Необходимо
обрабатывать только те случаи, которые влияют на
наше представление
Download