<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
	<id>https://www.wikicshse.ru/index.php?action=history&amp;feed=atom&amp;title=%D0%91%D0%B0%D0%B7%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%2F%D0%9B%D0%B0%D0%B1%D0%BE%D1%80%D0%B0%D1%82%D0%BE%D1%80%D0%BD%D0%B0%D1%8F_%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_4</id>
	<title>Базы данных/Лабораторная работа 4 - История изменений</title>
	<link rel="self" type="application/atom+xml" href="https://www.wikicshse.ru/index.php?action=history&amp;feed=atom&amp;title=%D0%91%D0%B0%D0%B7%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%2F%D0%9B%D0%B0%D0%B1%D0%BE%D1%80%D0%B0%D1%82%D0%BE%D1%80%D0%BD%D0%B0%D1%8F_%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_4"/>
	<link rel="alternate" type="text/html" href="https://www.wikicshse.ru/index.php?title=%D0%91%D0%B0%D0%B7%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85/%D0%9B%D0%B0%D0%B1%D0%BE%D1%80%D0%B0%D1%82%D0%BE%D1%80%D0%BD%D0%B0%D1%8F_%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_4&amp;action=history"/>
	<updated>2026-06-06T22:31:53Z</updated>
	<subtitle>История изменений этой страницы в вики</subtitle>
	<generator>MediaWiki 1.45.3</generator>
	<entry>
		<id>https://www.wikicshse.ru/index.php?title=%D0%91%D0%B0%D0%B7%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85/%D0%9B%D0%B0%D0%B1%D0%BE%D1%80%D0%B0%D1%82%D0%BE%D1%80%D0%BD%D0%B0%D1%8F_%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_4&amp;diff=1984&amp;oldid=prev</id>
		<title>imported&gt;ADKosm: /* Создание таблицы истории */</title>
		<link rel="alternate" type="text/html" href="https://www.wikicshse.ru/index.php?title=%D0%91%D0%B0%D0%B7%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85/%D0%9B%D0%B0%D0%B1%D0%BE%D1%80%D0%B0%D1%82%D0%BE%D1%80%D0%BD%D0%B0%D1%8F_%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_4&amp;diff=1984&amp;oldid=prev"/>
		<updated>2016-06-07T18:42:02Z</updated>

		<summary type="html">&lt;p&gt;&lt;span class=&quot;autocomment&quot;&gt;Создание таблицы истории&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Новая страница&lt;/b&gt;&lt;/p&gt;&lt;div&gt;Цели лабораторной работы:&lt;br /&gt;
* Знакомство с языком PL/pgSQL&lt;br /&gt;
* Создание процедур, функций и триггеров, обеспечивающих удобство работы с базой и обеспечение целостности&lt;br /&gt;
* Использование транзакция для обеспечения целостности&lt;br /&gt;
&lt;br /&gt;
== Зачем использовать PL/pgSQL ==&lt;br /&gt;
&lt;br /&gt;
PL/pgSQL - процедурный язык программирования, позволяющий выполнять несложные скрипты. Язык PL/pgSQL является расширенной версией стандарта языка PL/SQL. В PostgreSQL есть также возможность расширить синтаксис встроенного языка за счет интерпретаторов популярных языков: Python, Perl, Java, Lua, R и тд.&lt;br /&gt;
&lt;br /&gt;
Основное преимущество использования PL/pgSQL состоит в том, что скрипты выполняются непосредственно на сервере СУБД в отличии от любых типов взаимодействия, которые предполагают, что скрипт выполняется на стороне клиента, лишь через драйвер взаимодействуя с сервером, передавая ему запросы и получая ответы. Таким образом, PL/pgSQL востребован для операций с интенсивным вводом/выводом информации.&lt;br /&gt;
&lt;br /&gt;
К наиболее типичным задачам, реализуемым на PL/SQL относятся:&lt;br /&gt;
* Выполнение и контроль целостности операций с высокими требованиями к скорости исполнения (например, финансовые операции, событиями систем мониторинга)&lt;br /&gt;
* Аудит изменения данных: созранение истории изменения значений кортежей наиболее важных отношений в системе.&lt;br /&gt;
* Генерация разовых периодических отчетов, в которых аналитируется большой объем данных (для экономии времени пересылки данных между клиентом и сервером).&lt;br /&gt;
&lt;br /&gt;
Также к преимуществам использования PL/SQL является использование одного языка программирования для выполнения запросов и для выполнения инструкций.&lt;br /&gt;
&lt;br /&gt;
Как правило, язык PL/pgSQL не используют в задачах, где некритичны его преимущества из-за относительно сложного процесса поддержки и нагрузки на сервер СУБД.&lt;br /&gt;
&lt;br /&gt;
== Процедуры и функции ==&lt;br /&gt;
&lt;br /&gt;
Отличие процедур и функций состоит лишь в том, что процедуры не возвращают никаких значений, в отличии от функций. И процедуры и функции могут принимать входные параметры.&lt;br /&gt;
&lt;br /&gt;
Но в PostgreSQL нет различий в синтаксисе создания функций и процедур, обе создаются с помощью ключевых слов create function.&lt;br /&gt;
&lt;br /&gt;
Пример создания процедуры:&lt;br /&gt;
&lt;br /&gt;
  CREATE OR REPLACE FUNCTION add_movie(title VARCHAR(70), production_year INTEGER) &lt;br /&gt;
  RETURNS void AS $$&lt;br /&gt;
    DECLARE phonetic_code VARCHAR(70);&lt;br /&gt;
  BEGIN&lt;br /&gt;
    SELECT soundex(title) INTO phonetic_code;&lt;br /&gt;
    RAISE NOTICE &amp;#039;The phonetic code for % is %&amp;#039;, title, phonetic_code;&lt;br /&gt;
    INSERT INTO title (title, production_year, phonetic_code, kind_id) VALUES (title, production_year, phonetic_code, 1);&lt;br /&gt;
    COMMIT;&lt;br /&gt;
  END;&lt;br /&gt;
  $$ LANGUAGE plpgsql;&lt;br /&gt;
&lt;br /&gt;
Тело процедуры находится между BEGIN и END. При этом внутри тела могут быть транзакции, которые также будут обернуты в BEGIN-END.&lt;br /&gt;
&lt;br /&gt;
В примере также используется soundex, которая доступна после подключения модуля (выполните до создания процедуры):&lt;br /&gt;
&lt;br /&gt;
  imdb=# create extension fuzzystrmatch;&lt;br /&gt;
&lt;br /&gt;
Чтобы выполнить созданную процедуру, выберите ее:&lt;br /&gt;
&lt;br /&gt;
  select add_movie(&amp;#039;How I will pass all the exams&amp;#039;, 2016);&lt;br /&gt;
&lt;br /&gt;
Этот пример сейчас скорее всего не сработает с ERROR: duplicate key value violates unique constraint &amp;quot;title_pkey&amp;quot;. В конце этой части есть процедура, которая поможет вам добавить фильм.&lt;br /&gt;
&lt;br /&gt;
=== Определение переменных ===&lt;br /&gt;
&lt;br /&gt;
Определение переменных происходит с помощью команды DECLARE, после которой нужно указать имя и тип. Также можно сразу присвоить значение:&lt;br /&gt;
&lt;br /&gt;
  DECLARE production_year INTEGER := 2016;&lt;br /&gt;
&lt;br /&gt;
Определять переменные можно между BEGIN и END - в этом случае они действуют только, или сразу после определения процедуры (перед BEGIN как в примере выше), тогда переменная будет глобальной и доступна во всей процедуре.&lt;br /&gt;
&lt;br /&gt;
Чтобы записать в переменную кортеж или одно значение, нужно использовать SELECT ... INTO var:&lt;br /&gt;
&lt;br /&gt;
   SELECT count(1) INTO actors_num from cast_info WHERE ...&lt;br /&gt;
&lt;br /&gt;
=== Управляющие конструкции ===&lt;br /&gt;
&lt;br /&gt;
При выборке данных в переменную, можно написать разные сценарии в зависимости от количества кортежей, которые возвращает запрос:&lt;br /&gt;
&lt;br /&gt;
  BEGIN&lt;br /&gt;
    SELECT * INTO movie FROM title WHERE title like &amp;#039;%&amp;#039;||movie_name||&amp;#039;%&amp;#039;;&lt;br /&gt;
    EXCEPTION&lt;br /&gt;
        WHEN NO_DATA_FOUND THEN&lt;br /&gt;
            RAISE EXCEPTION &amp;#039;movie % not found&amp;#039;, movie_name;&lt;br /&gt;
        WHEN TOO_MANY_ROWS THEN&lt;br /&gt;
            RAISE EXCEPTION &amp;#039;movie % not unique&amp;#039;, movie_name;&lt;br /&gt;
  END;&lt;br /&gt;
&lt;br /&gt;
В этом примере также есть конструкция title like &amp;#039;%&amp;#039;||movie_name||&amp;#039;%&amp;#039;, в которой movie_name - переменная, хранящая часть название фильма. Конкатенация строк выполняется оператором ||. &lt;br /&gt;
&lt;br /&gt;
Также доступны циклы и условия:&lt;br /&gt;
&lt;br /&gt;
    FOR movie_row IN SELECT * FROM title limit 10 LOOP&lt;br /&gt;
        RAISE NOTICE &amp;#039;Currently working on movie %s ...&amp;#039;, quote_ident(movie_row.title);&lt;br /&gt;
        IF movie_row.production_year &amp;gt; 2000 THEN&lt;br /&gt;
            RAISE NOTICE &amp;#039;This movie is of this century&amp;#039;;&lt;br /&gt;
        END IF;&lt;br /&gt;
    END LOOP;&lt;br /&gt;
&lt;br /&gt;
Эта конструкция проходит по кортежам, возвращаемым запросом SELECT * FROM title limit 10, для каждого выводит его название и, если фильм выпущен после 2000 года, то сообщение о его новизне.&lt;br /&gt;
&lt;br /&gt;
=== Использование курсоров ===&lt;br /&gt;
&lt;br /&gt;
Курсоры позволяют не выбирать в память сразу много данных, а использовать указатель на записи и переходить к следующей или предыдущей записи, считывая в память только ее.&lt;br /&gt;
&lt;br /&gt;
Определения курсора:&lt;br /&gt;
&lt;br /&gt;
  DECLARE&lt;br /&gt;
    curs1 refcursor; -- будет указан в будущем&lt;br /&gt;
    curs2 CURSOR FOR SELECT * FROM cast_info; -- сразу же указывает на запрос (запрос не выполняется в момент определения)&lt;br /&gt;
&lt;br /&gt;
Чтобы начать пользоваться курсором, его нужно открыть:&lt;br /&gt;
&lt;br /&gt;
  OPEN curs1 FOR SELECT * FROM cast_info;  -- если курсор не был привязан к запросу ранее&lt;br /&gt;
  OPEN curs2; -- для запросов, привязанных к запросу&lt;br /&gt;
&lt;br /&gt;
После этого навигация курсора может быть следующей:&lt;br /&gt;
&lt;br /&gt;
  FETCH curs1 INTO rowvar;  -- получить следующий кортеж запроса&lt;br /&gt;
  MOVE curs1;  -- то же самое&lt;br /&gt;
  MOVE LAST FROM curs3;  -- получить последний кортеж запроса&lt;br /&gt;
  MOVE RELATIVE -2 FROM curs4;  -- сдвинуть относительно текущего положения запроса на 2 кортежа назад&lt;br /&gt;
  MOVE FORWARD 2 FROM curs4;  -- сдвинуть курсор на 2 кортежа вперед.&lt;br /&gt;
&lt;br /&gt;
Когда курсор не нужен или требуется его переоткрыть, то используется команда:&lt;br /&gt;
&lt;br /&gt;
  CLOSE curs1;&lt;br /&gt;
&lt;br /&gt;
=== Логирование и обработка ошибок ===&lt;br /&gt;
&lt;br /&gt;
Вывод информационного сообщения (при этом процедура продолжит работу дальше):&lt;br /&gt;
&lt;br /&gt;
  RAISE NOTICE &amp;#039;Data processing...&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Команда завершающая процедуру или транзакцию с ошибкой:&lt;br /&gt;
&lt;br /&gt;
  RAISE EXCEPTION &amp;#039;Something went wrong&amp;#039;&lt;br /&gt;
&lt;br /&gt;
=== Пример полезной для администрирования процедуры ===&lt;br /&gt;
&lt;br /&gt;
Попробуем для всех отношений, у которых есть последовательности генерации первичных ключей установить значения максимальных идентификаторов, чтобы эти последовательности далее работали корректно. Для этого понадобится &lt;br /&gt;
&lt;br /&gt;
* выбрать все последовательности, связать их с таблицами&lt;br /&gt;
* для каждой пары выбрать подходящее значение&lt;br /&gt;
* установить это значение как текущее для последовательности.&lt;br /&gt;
&lt;br /&gt;
   CREATE OR REPLACE FUNCTION fix_sequences() &lt;br /&gt;
   RETURNS void AS $$&lt;br /&gt;
    DECLARE max_id INTEGER;&lt;br /&gt;
    DECLARE t RECORD;  -- переменная для обхода кортежей выборки&lt;br /&gt;
   BEGIN&lt;br /&gt;
    FOR t IN (select table_name from pg_class s &lt;br /&gt;
    join information_schema.tables on tables.table_schema = &amp;#039;public&amp;#039; and table_name=replace(relname, &amp;#039;_id_seq&amp;#039;, &amp;amp;apos;&amp;amp;apos; )&lt;br /&gt;
    where s.relkind=&amp;#039;S&amp;#039;) LOOP&lt;br /&gt;
      EXECUTE &amp;#039;select max(id) from &amp;#039;||t.table_name into max_id; -- выполняет запрос, сформированный в строке и передает результат в max_id&lt;br /&gt;
      raise notice &amp;#039;max id for % is %&amp;#039;, t.table_name, max_id;&lt;br /&gt;
      if max_id is not null then&lt;br /&gt;
         -- выполняет запрос увеличения текущего значения последовательности&lt;br /&gt;
         EXECUTE &amp;#039;SELECT setval(&amp;amp;apos;&amp;amp;apos;&amp;#039;||t.table_name||&amp;#039;_id_seq&amp;#039;&amp;amp;apos;, &amp;#039;||max_id||&amp;#039;, true)&amp;#039;;&lt;br /&gt;
      end if;&lt;br /&gt;
    END LOOP;&lt;br /&gt;
   END;&lt;br /&gt;
   $$ LANGUAGE plpgsql;&lt;br /&gt;
&lt;br /&gt;
  select fix_sequences();  -- вызов процедуры&lt;br /&gt;
&lt;br /&gt;
== Триггеры ==&lt;br /&gt;
&lt;br /&gt;
Триггеры - это процедуры, которые срабатывают в случае наступления в системе какого-либо события. В триггерах доступны переменные, связанные с происходящим событием.&lt;br /&gt;
&lt;br /&gt;
=== Типы триггеров ===&lt;br /&gt;
&lt;br /&gt;
Триггер может быть привязан к командам UPDATE, INSERT и DELETE конкретных отношений. Также триггер может быть запущен как до выполнения указанной операции, так и после. Например:&lt;br /&gt;
&lt;br /&gt;
  CREATE TRIGGER cool_trigger_name BEFORE INSERT OR UPDATE ON movie_info&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
Этот триггер выполнится перед операциями добавления и обновления данных в таблице movie_info.&lt;br /&gt;
&lt;br /&gt;
В триггерах можно обращаться к изменяемым данным. также триггер может обрабатывать сразу все изменяемые кортежи в цикле. Обычно в каждой итерации вызывают процедуру, которая не принимает аргументов (в ней доступны переменные триггера) и возвращает объект триггера:&lt;br /&gt;
&lt;br /&gt;
  CREATE OR REPLACE FUNCTION check_rows() RETURNS trigger AS $$&lt;br /&gt;
  BEGIN&lt;br /&gt;
    IF NEW.title is null THEN&lt;br /&gt;
      RAISE EXCEPTION &amp;#039;Invalid title&amp;#039;; &lt;br /&gt;
    END IF;&lt;br /&gt;
    RETURN NEW;&lt;br /&gt;
  END;&lt;br /&gt;
  $$ LANGUAGE plpgsql;&lt;br /&gt;
&lt;br /&gt;
  CREATE TRIGGER before_insert_check BEFORE INSERT OR UPDATE ON title&lt;br /&gt;
    FOR EACH ROW EXECUTE PROCEDURE check_rows();&lt;br /&gt;
&lt;br /&gt;
Старые и новые значения данных доступны соответственно через OLD.* и NEW.* для каждого кортежа.&lt;br /&gt;
&lt;br /&gt;
== Транзакции ==&lt;br /&gt;
&lt;br /&gt;
Транзакции нужны для того, чтобы проводить сложные операции атомарно: в случае успеха фиксировать все изменения, в случае неуспеха не фиксировать ни одно изменение транзакции.&lt;br /&gt;
&lt;br /&gt;
Управление транзакцией выполняется с помощью команд BEGIN, SAVEPOINT, ROLLBACK и COMMIT. Пример сценария сложной транзакции:&lt;br /&gt;
&lt;br /&gt;
  BEGIN;  -- начало транзакции&lt;br /&gt;
    -- выполнение операций&lt;br /&gt;
  SAVEPOINT my_savepoint; -- сохранить точку, на которую можно потом вернуться, &lt;br /&gt;
                          -- но изменения в базу все еще не вносятся&lt;br /&gt;
    -- выполнение операций&lt;br /&gt;
  ROLLBACK TO my_savepoint;  -- после сохранения произошла ошибка, откат на состояние my_savepoint&lt;br /&gt;
    -- выполнение другого сценария&lt;br /&gt;
  IF check_ok() THEN:&lt;br /&gt;
    COMMIT;  -- фиксация результата&lt;br /&gt;
  ELSE&lt;br /&gt;
    ROLLBACK;&lt;br /&gt;
  END IF;&lt;br /&gt;
&lt;br /&gt;
=== Создание таблицы истории ===&lt;br /&gt;
&lt;br /&gt;
Для задания понадобится таблица истории, сделать ее проще всего унаследовав от существующей, а затем убрав наследование и добавив колонку для времени изменения:&lt;br /&gt;
&lt;br /&gt;
  CREATE TABLE tablename_history () INHERITS (tablename);  -- копирует структуру таблицы без записей&lt;br /&gt;
  ALTER TABLE tablename_history NO INHERIT tablename;&lt;br /&gt;
&lt;br /&gt;
== Задания лабораторной работы ==&lt;br /&gt;
1. Сделать таблицу с историей изменений company_name, в которую при обновлении через триггер записываются прежние значения и дата окончания их действий (дата обновления). Сделайте запрос, который показывает значения для конкретного кортежа на заданный момент времени.&lt;br /&gt;
&lt;br /&gt;
2. Создать функцию с входным параметром - имя актера. Если актер с таким именем не найден, функция должна вернуть 0. Если найден, то вычислить через person_info birth date (info_type_id=21) и death date (info_type_id=23) возраст актера. Если актер еще не умер, то вычислить, сколько сейчас лет. Функция должна вернуть целое число прожитых лет.&lt;br /&gt;
&lt;br /&gt;
3. Используя функцию, созданную ранее, создайте процедуру, которая по имени актера выводит в лог текст следующего содержания:&lt;br /&gt;
  Name: ...&lt;br /&gt;
  Nicknames: nickname1, nickname2, ... строка выводится только если у человека есть клички, выводятся все клички через запятую&lt;br /&gt;
  Age: ... вычисляется из функции&lt;br /&gt;
  First appear: ... фильм и год выхода, в котором актер впервые снялся.&lt;br /&gt;
&lt;br /&gt;
Если актер не найден или не удалось определить его первую роль, то процедура должна завершиться с ошибкой и вывести в лог &amp;quot;Invalid data&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Защита лабораторной работы ==&lt;br /&gt;
* Показать код созданных триггера, функции и процедуры&lt;br /&gt;
* Продемонстрировать их работу&lt;br /&gt;
* По просьбе преподавателя прокомментировать ход выполнения кода.&lt;br /&gt;
&lt;br /&gt;
== Дополнительно ==&lt;br /&gt;
* http://www.postgresql.org/docs/9.3/static/plpgsql.html&lt;br /&gt;
* http://postgres.cz/wiki/PL/pgSQL_%28en%29 - примеры процедур, функций и триггеров&lt;br /&gt;
* http://www.postgresql.org/docs/9.3/static/tutorial-transactions.html&lt;br /&gt;
* http://www.postgresql.org/docs/9.3/static/plpython.html - позволяет писать хранимые процедуры на питоне, нужно включить расширение&lt;/div&gt;</summary>
		<author><name>imported&gt;ADKosm</name></author>
	</entry>
</feed>