<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
	<id>https://wiki.cs.hse.ru/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=ADKosm</id>
	<title>Wiki - Факультет компьютерных наук - Вклад [ru]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.cs.hse.ru/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=ADKosm"/>
	<link rel="alternate" type="text/html" href="https://wiki.cs.hse.ru/%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/ADKosm"/>
	<updated>2026-09-21T14:28:45Z</updated>
	<subtitle>Вклад</subtitle>
	<generator>MediaWiki 1.43.9</generator>
	<entry>
		<id>https://wiki.cs.hse.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_5&amp;diff=19673</id>
		<title>Базы данных/Лабораторная работа 5</title>
		<link rel="alternate" type="text/html" href="https://wiki.cs.hse.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_5&amp;diff=19673"/>
		<updated>2016-06-08T10:27:42Z</updated>

		<summary type="html">&lt;p&gt;ADKosm: /* Подокументное преобразование данных */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Задачи лабораторной работы:&lt;br /&gt;
* Установить MongoDB&lt;br /&gt;
* Рассмотреть различия в моделировании данных для реляционных БД и документоориентированных.&lt;br /&gt;
* Попрактиковаться в составлении запросов к MongoDB: добавление, обновление, селекция, фильтрация, агрегация.&lt;br /&gt;
* Настроить репликацию данных, инициировать отключение мастер-узла в процессе интенсивного обновления данных, проанализировать действия сервера и проверить целостность измененных данных.&lt;br /&gt;
&lt;br /&gt;
== Установка MongoDB ==&lt;br /&gt;
&lt;br /&gt;
Для лабораторных работ достаточно версии 2.4+&lt;br /&gt;
&lt;br /&gt;
* Ubuntu: sudo apt-get install mongodb&lt;br /&gt;
* Mac OS: brew install mongodb [http://www.mongodbspain.com/en/2014/11/06/install-mongodb-on-mac-os-x-yosemite/ Подробнее]&lt;br /&gt;
* Windows: Скачать установщик: https://www.mongodb.com/download-center#community&lt;br /&gt;
&lt;br /&gt;
== Импорт данных ==&lt;br /&gt;
Коллекции, содержащие фильмы и длинные текстовые факты о фильмах:&lt;br /&gt;
* https://yadi.sk/d/Y7r672PvsEyaC&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ссылки на отдельные файлы:&lt;br /&gt;
* https://yadi.sk/d/_UIdFUhhsF8SC&lt;br /&gt;
* https://yadi.sk/d/sSuDjY3fsF8S7&lt;br /&gt;
&lt;br /&gt;
Для импорта коллекций в базу movies выполните:&lt;br /&gt;
&lt;br /&gt;
  bzip2 -dc movies.bson.bz2 | mongoimport -d movies -c Movie&lt;br /&gt;
  bzip2 -dc moviesdocs.bson.bz2 | mongoimport -d movies -c MovieDoc&lt;br /&gt;
&lt;br /&gt;
Импортированная коллекция займет примерно &#039;&#039;&#039;6Гб&#039;&#039;&#039; на диске.&lt;br /&gt;
&lt;br /&gt;
Чтобы проверить корректность импорта, зайдите в консоль MongoDB:&lt;br /&gt;
&lt;br /&gt;
  mongo&lt;br /&gt;
&lt;br /&gt;
Показать базы данных:&lt;br /&gt;
&lt;br /&gt;
  show dbs;&lt;br /&gt;
&lt;br /&gt;
Переключиться на базу данных (если ее нет, то создастся):&lt;br /&gt;
&lt;br /&gt;
  use movies&lt;br /&gt;
&lt;br /&gt;
Показать коллекции текущей базы данных:&lt;br /&gt;
&lt;br /&gt;
  show collections;&lt;br /&gt;
&lt;br /&gt;
== Экспорт данных ==&lt;br /&gt;
&lt;br /&gt;
В случае, если понадобится экспортировать данные, то сделать это можно (для версий 2.*) по коллекциям, запуская:&lt;br /&gt;
&lt;br /&gt;
  mongoexport --db movies -c MovieDoc -o - | bzip2 - &amp;gt; moviesdocs.bson.bz2&lt;br /&gt;
  mongoexport --db movies -c Movie -o - | bzip2 - &amp;gt; movies.bson.bz2&lt;br /&gt;
&lt;br /&gt;
Если вы создадите индексы, то они будут лежать в отдельной коллекции, ее также нужно экспортировать.&lt;br /&gt;
&lt;br /&gt;
== Моделирование данных в MongoDB ==&lt;br /&gt;
Так как MongoDB не стремится экономить место на диске, а наоборот активно резервирует место под обновления данных, итоговая база занимает более 30Гб. В данной лабораторной работе рассматривается только сущность фильмов и связанные с ней факты (информация аналогичная хранимой в title, movie_info, keywords).&lt;br /&gt;
&lt;br /&gt;
Схема модели данных в MongoDB:&lt;br /&gt;
&lt;br /&gt;
https://www.packtpub.com/sites/default/files/new_blog_images/147/1-flatmodel.gif&lt;br /&gt;
&lt;br /&gt;
В дампе базы есть коллекции: &lt;br /&gt;
* Movie - для заголовков фильмов, ключевых слов и некоторых фактов&lt;br /&gt;
* MovieDoc - для длинных текстовых фактов (описания, цитаты и тд)&lt;br /&gt;
&lt;br /&gt;
=== Коллекция Movie ===&lt;br /&gt;
&lt;br /&gt;
Это основная коллекция, ключом в которой является отформатированная строка, состоящая из: названия фильма, года выпуска, названия эпизода (для сериалов). Пример поиска сериала и эпизода по ключу:&lt;br /&gt;
&lt;br /&gt;
  db.Movie.find({&amp;quot;_id&amp;quot; : &amp;quot;\&amp;quot;12 oz. Mouse\&amp;quot; (2005)&amp;quot;})  // выбрать сериал или фильм &lt;br /&gt;
  db.Movie.find({&amp;quot;SeriesID&amp;quot; : &amp;quot;\&amp;quot;12 oz. Mouse\&amp;quot; (2005)&amp;quot;}) // выбрать сериал и все эпизоды &lt;br /&gt;
  db.Movie.find({&amp;quot;_id&amp;quot; : &amp;quot;\&amp;quot;12 oz. Mouse\&amp;quot; (2005) {Adventure Mouse (#1.7)}&amp;quot;})  // выбрать конкретный эпизод&lt;br /&gt;
&lt;br /&gt;
Для удобства восприятия информации используйте .pretty():&lt;br /&gt;
&lt;br /&gt;
  &amp;gt; db.Movie.find({&amp;quot;_id&amp;quot; : &amp;quot;\&amp;quot;12 oz. Mouse\&amp;quot; (2005)&amp;quot;}).pretty()&lt;br /&gt;
  {&lt;br /&gt;
    &amp;quot;AltTitles&amp;quot; : [&lt;br /&gt;
        &amp;quot;\&amp;quot;12 Ouns Mouce\&amp;quot; (2005)&amp;quot;,&lt;br /&gt;
        &amp;quot;\&amp;quot;Oz. Mo\&amp;quot; (2005)&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;Countries&amp;quot; : [&lt;br /&gt;
        &amp;quot;USA&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;Genres&amp;quot; : [&lt;br /&gt;
        &amp;quot;Animation&amp;quot;,&lt;br /&gt;
        &amp;quot;Comedy&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;Keywords&amp;quot; : [&lt;br /&gt;
        &amp;quot;abbreviation-in-title&amp;quot;,&lt;br /&gt;
        &amp;quot;absurdism&amp;quot;,&lt;br /&gt;
        &amp;quot;adult-animation&amp;quot;,&lt;br /&gt;
        &amp;quot;animal-in-title&amp;quot;,&lt;br /&gt;
        &amp;quot;avant-garde&amp;quot;,&lt;br /&gt;
        &amp;quot;beer&amp;quot;,&lt;br /&gt;
        &amp;quot;character-name-in-title&amp;quot;,&lt;br /&gt;
        &amp;quot;digit-in-title&amp;quot;,&lt;br /&gt;
        &amp;quot;drunkenness&amp;quot;,&lt;br /&gt;
        &amp;quot;late-night&amp;quot;,&lt;br /&gt;
        &amp;quot;mouse&amp;quot;,&lt;br /&gt;
        &amp;quot;non-sequitur&amp;quot;,&lt;br /&gt;
        &amp;quot;number-in-title&amp;quot;,&lt;br /&gt;
        &amp;quot;period-in-title&amp;quot;,&lt;br /&gt;
        &amp;quot;surrealism&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;Languages&amp;quot; : [&lt;br /&gt;
        &amp;quot;English&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;Locations&amp;quot; : [&lt;br /&gt;
        &amp;quot;Atlanta, Georgia, USA&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;MovieID&amp;quot; : &amp;quot;\&amp;quot;12 oz. Mouse\&amp;quot; (2005)&amp;quot;,&lt;br /&gt;
    &amp;quot;Parental&amp;quot; : {&lt;br /&gt;
        &amp;quot;Certificates&amp;quot; : [&lt;br /&gt;
            &amp;quot;Australia:MA15+&amp;quot;,&lt;br /&gt;
            &amp;quot;Canada:14+\t(TV rating)&amp;quot;,&lt;br /&gt;
            &amp;quot;New Zealand:M&amp;quot;,&lt;br /&gt;
            &amp;quot;USA:TV-14&amp;quot;,&lt;br /&gt;
            &amp;quot;USA:TV-MA\t(one episode)&amp;quot;&lt;br /&gt;
        ]&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;Rating&amp;quot; : {&lt;br /&gt;
        &amp;quot;RatingDist&amp;quot; : &amp;quot;1000001103&amp;quot;,&lt;br /&gt;
        &amp;quot;Rating&amp;quot; : &amp;quot;6.9&amp;quot;,&lt;br /&gt;
        &amp;quot;RatingVotes&amp;quot; : &amp;quot;1365&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;ReleaseYear&amp;quot; : &amp;quot;2005&amp;quot;,&lt;br /&gt;
    &amp;quot;RunningTime&amp;quot; : &amp;quot;15&amp;quot;,&lt;br /&gt;
    &amp;quot;SeriesEndYear&amp;quot; : &amp;quot;&amp;quot;,&lt;br /&gt;
    &amp;quot;SeriesID&amp;quot; : &amp;quot;\&amp;quot;12 oz. Mouse\&amp;quot; (2005)&amp;quot;,&lt;br /&gt;
    &amp;quot;SeriesType&amp;quot; : &amp;quot;S&amp;quot;,&lt;br /&gt;
    &amp;quot;Technical&amp;quot; : {&lt;br /&gt;
        &amp;quot;Colors&amp;quot; : [&lt;br /&gt;
            &amp;quot;Color&amp;quot;&lt;br /&gt;
        ]&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;_id&amp;quot; : &amp;quot;\&amp;quot;12 oz. Mouse\&amp;quot; (2005)&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Расшифровка SeriesType: &lt;br /&gt;
* F - фильм&lt;br /&gt;
* S - сериал&lt;br /&gt;
* E - эпизод&lt;br /&gt;
&lt;br /&gt;
=== Индексы ===&lt;br /&gt;
&lt;br /&gt;
В предлагаемом дампе нет индексов (только первичный ключ _id), в ходе выполнения заданий они могут понадобиться.&lt;br /&gt;
&lt;br /&gt;
Просмотр существующих индексов в коллекции:&lt;br /&gt;
&lt;br /&gt;
  db.collection_name.getIndexes()&lt;br /&gt;
&lt;br /&gt;
Функция просмотра всех индексов для всех коллекций:&lt;br /&gt;
&lt;br /&gt;
  db.getCollectionNames().forEach(function(collection) {&lt;br /&gt;
     indexes = db[collection].getIndexes();&lt;br /&gt;
     print(&amp;quot;Indexes for &amp;quot; + collection + &amp;quot;:&amp;quot;);&lt;br /&gt;
     printjson(indexes);&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
Создание индексов:&lt;br /&gt;
&lt;br /&gt;
  db.collection_name.createIndex( { field_name: 1 } )&lt;br /&gt;
  db.collection_name.createIndex( { field_name: &amp;quot;hashed&amp;quot; } )  // хэш-индексы&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Подокументное преобразование данных ===&lt;br /&gt;
&lt;br /&gt;
Недостатком предлагаемой базы является то, что все атрибуты фильмов по-прежнему строковые, хотя некоторые, например RunningTime или Rating.RatingVotes содержат только числа. Это часто встречаемая ситуация, поэтому далее рассматривается пример функции, которая помогает выполнять подобные преобразования.&lt;br /&gt;
&lt;br /&gt;
Конвертировать продолжительность фильма (задана для всех):&lt;br /&gt;
  db.Movie.find().forEach(function(data) {&lt;br /&gt;
     db.Movie.update({&lt;br /&gt;
         &amp;quot;_id&amp;quot;: data._id&lt;br /&gt;
     }, {&lt;br /&gt;
         &amp;quot;$set&amp;quot;: {&lt;br /&gt;
             &amp;quot;RunningTime&amp;quot;: parseInt(data.RunningTime)&lt;br /&gt;
         }&lt;br /&gt;
     });&lt;br /&gt;
 });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Конвертировать рейтинг там, где он задан:&lt;br /&gt;
 &lt;br /&gt;
  db.Movie.find({&amp;quot;Rating.Rating&amp;quot;: {$exists: true}, &amp;quot;Rating.RatingVotes&amp;quot;: {$exists: true}}).forEach(function(data) {&lt;br /&gt;
    db.Movie.update({&lt;br /&gt;
        &amp;quot;_id&amp;quot;: data._id&lt;br /&gt;
    }, {&lt;br /&gt;
        &amp;quot;$set&amp;quot;: {&lt;br /&gt;
            &amp;quot;Rating.Rating&amp;quot;: parseFloat(data.Rating.Rating),&lt;br /&gt;
            &amp;quot;Rating.RatingVotes&amp;quot;: parseInt(data.Rating.RatingVotes)&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
  })&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Задание:&#039;&#039;&#039; Попробуйте добавить фильмам атрибут &amp;quot;длина названия&amp;quot;, его нельзя вычислить с помощью обычной команды find, но можно сохранить эту информацию заранее. И также попробуйте преобразовать годы выхода сериалов для тех записей, где они указаны.&lt;br /&gt;
&lt;br /&gt;
=== Дополнительно ===&lt;br /&gt;
* Подробное описание скрипта и модели данных: https://www.packtpub.com/books/content/mongo-goes-to-the-movies&lt;br /&gt;
&lt;br /&gt;
== Язык запросов MongoDB ==&lt;br /&gt;
&lt;br /&gt;
=== Базовые операции ===&lt;br /&gt;
&lt;br /&gt;
==== Выборка ====&lt;br /&gt;
&lt;br /&gt;
Поиск по текстовым полям поддерживает синтаксис регулярных выражений, для нечеткого поиска нужно заключить выражение в /title/ (аналог &amp;quot;%title%&amp;quot;). Пример:&lt;br /&gt;
&lt;br /&gt;
  db.Movie.find({&amp;quot;MovieID&amp;quot;: /Matrix.*1999/})&lt;br /&gt;
&lt;br /&gt;
Чтобы вывести только нужные поля, используйте второй параметр функции find:&lt;br /&gt;
&lt;br /&gt;
  &amp;gt; db.Movie.find({&amp;quot;_id&amp;quot; : &amp;quot;The Matrix (1999)&amp;quot;}, {Genres: 1, Business: 1}).pretty()&lt;br /&gt;
&lt;br /&gt;
Поиск по значениям в списках не отличается от обычного:&lt;br /&gt;
&lt;br /&gt;
  db.Movie.find({&amp;quot;Genres&amp;quot;: &amp;quot;Action&amp;quot;}).limit(3)&lt;br /&gt;
&lt;br /&gt;
==== Обновление ====&lt;br /&gt;
&lt;br /&gt;
Добавить новый документ:&lt;br /&gt;
  db.Movie.insert({_id: &amp;quot;\&amp;quot;How I am getting 10 for db exam\&amp;quot; (2016)&amp;quot;, &amp;quot;MovieID&amp;quot; : &amp;quot;\&amp;quot;How I am getting 10 for db exam\&amp;quot; (2016)&amp;quot;, &amp;quot;Countries&amp;quot; : [ &amp;quot;Russia&amp;quot; ], &amp;quot;Genres&amp;quot; : [ &amp;quot;Drama&amp;quot;, &amp;quot;Comedy&amp;quot; ], &amp;quot;Keywords&amp;quot;: [ &amp;quot;study&amp;quot;, &amp;quot;working-hard&amp;quot;, &amp;quot;success&amp;quot; ], &amp;quot;ReleaseYear&amp;quot; : 2016})&lt;br /&gt;
&lt;br /&gt;
  db.Movie.insert({_id: &amp;quot;\&amp;quot;How I am getting 10 for project\&amp;quot; (2016)&amp;quot;, &amp;quot;MovieID&amp;quot; : &amp;quot;\&amp;quot;How I am getting 10 for db exam\&amp;quot; (2016)&amp;quot;, &amp;quot;Countries&amp;quot; : [ &amp;quot;Russia&amp;quot; ], &amp;quot;Genres&amp;quot; : [ &amp;quot;Drama&amp;quot;, &amp;quot;Comedy&amp;quot; ], &amp;quot;Keywords&amp;quot;: [ &amp;quot;study&amp;quot;, &amp;quot;working-hard&amp;quot;, &amp;quot;success&amp;quot; ], &amp;quot;ReleaseYear&amp;quot; : 2016})&lt;br /&gt;
&lt;br /&gt;
Обновить атрибут для всех документов по результатам поиска:&lt;br /&gt;
&lt;br /&gt;
  db.Movie.update({MovieID: /How I am getting 10 for/}, {$set: {&amp;quot;Locations&amp;quot; : [ &amp;quot;Moscow, Russia&amp;quot; ]}}, multi=true)&lt;br /&gt;
&lt;br /&gt;
* Первый аргумент - условия выборки обновляемых объектов&lt;br /&gt;
* Второй аргумент - что именно делать&lt;br /&gt;
* multi=true (по умолчанию false), обновляет все найденные объекты или только первый&lt;br /&gt;
&lt;br /&gt;
Добавить элемент в список документа:&lt;br /&gt;
&lt;br /&gt;
  db.Movie.update({MovieID: /How I am getting 10 for/}, {$push: {&amp;quot;Keywords&amp;quot;: &amp;quot;true-story&amp;quot;}}, multi=true)&lt;br /&gt;
&lt;br /&gt;
==== Дополнительно ====&lt;br /&gt;
* https://docs.mongodb.com/manual/crud/&lt;br /&gt;
&lt;br /&gt;
=== Aggregation Framework ===&lt;br /&gt;
&lt;br /&gt;
Для выполнения агрегирующих запросов в MongoDB используется команда aggregate.&lt;br /&gt;
&lt;br /&gt;
Сама агрегация выглядит как pipeline для данных, проходя через шаги, данные преобразуются (группируются, фильтруются и тд) в зависимости от команды этого шага. Пример:&lt;br /&gt;
&lt;br /&gt;
https://docs.mongodb.com/manual/_images/aggregation-pipeline.png&lt;br /&gt;
&lt;br /&gt;
Также можно разгруппировать списки в документах ($unwind).&lt;br /&gt;
&lt;br /&gt;
Чтобы отделять переменные внутри шагов от строк используется префикс $.&lt;br /&gt;
&lt;br /&gt;
Найти документы и разгруппировать ($unwind) их по ключевым словам:&lt;br /&gt;
&lt;br /&gt;
  db.Movie.aggregate({$match: {MovieID: /How I am getting 10 for/}}, &lt;br /&gt;
    {$unwind: &amp;quot;$Keywords&amp;quot;})&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Переформировать список выбираемых атрибутов ($project) аналогично второму параметру в find:&lt;br /&gt;
&lt;br /&gt;
  db.Movie.aggregate({$match: {MovieID: /How I am getting 10 for/}}, &lt;br /&gt;
    {$project: {&amp;quot;MovieID&amp;quot;: 1, &amp;quot;Keywords&amp;quot;: 1, &amp;quot;_id&amp;quot;: 0} }, &lt;br /&gt;
    {$unwind: &amp;quot;$Keywords&amp;quot;})&lt;br /&gt;
&lt;br /&gt;
Сгруппировать по атрибуту и применить агрегирующую функцию:&lt;br /&gt;
&lt;br /&gt;
  db.Movie.aggregate({$match: {MovieID: /How I am getting 10 for/}}, &lt;br /&gt;
    {$project: {&amp;quot;MovieID&amp;quot;: 1, &amp;quot;Keywords&amp;quot;: 1, &amp;quot;_id&amp;quot;: 0} }, &lt;br /&gt;
    {$unwind: &amp;quot;$Keywords&amp;quot;}, &lt;br /&gt;
    {$group: {_id: &amp;quot;$Keywords&amp;quot;, &amp;quot;MoviesList&amp;quot;: {$push: &amp;quot;$MovieID&amp;quot;} }})&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Дополнительно ====&lt;br /&gt;
* https://docs.mongodb.com/manual/aggregation/&lt;br /&gt;
&lt;br /&gt;
== Репликации MongoDB ==&lt;br /&gt;
&lt;br /&gt;
=== Принцип работы репликаций в MongoDB ===&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;
* https://docs.mongodb.com/manual/core/replication-introduction/&lt;br /&gt;
&lt;br /&gt;
== Задания на защиту ==&lt;br /&gt;
1. Составить запросы: &lt;br /&gt;
* Для каждого года из первой декады XXI века посчитать количество снятых фильмов.&lt;br /&gt;
* Выбрать топ-10 наиболее популярных ключевых слов для фильмов заданной страны.&lt;br /&gt;
&lt;br /&gt;
2. Составить свою формулу рейтинга (общего для всех фильмов или по какому-либо фильтру), используя параметры словарей Movie.Rating (Rating - среднее значение оценкой, RatingVotes - количество оценок) или любые другие.&lt;br /&gt;
&lt;br /&gt;
Для примера вот формула [http://www.imdb.com/chart/top Top 250 IMDb Movies]&lt;br /&gt;
&lt;br /&gt;
  ранг фильма = (v/(v+k))*X + (k/(v+k))*C&lt;br /&gt;
  &lt;br /&gt;
  где:&lt;br /&gt;
  &lt;br /&gt;
    X = рейтинг фильма&lt;br /&gt;
    v = количество голосов&lt;br /&gt;
    k = минимальное количество голосов, чтобы попасть в список (для данного списка 25000)&lt;br /&gt;
    C = средний рейтинг для всех фильмов в этом списке (для данного списка 6.90)&lt;br /&gt;
&lt;br /&gt;
MongoDB поддерживает многие арифметические операции: https://docs.mongodb.com/manual/reference/operator/aggregation-arithmetic/&lt;br /&gt;
&lt;br /&gt;
Данное задание можно выполнить не одним запросом через агрегацию, а используя функцию на JavaScript, которая выполняется в консоле MongoDB.&lt;br /&gt;
&lt;br /&gt;
3. Написать скрипт на bash или любом другом скриптовом языке, который запускает сервер MongoDB в режиме реплики, начинает интенсивно изменять данные, определяет мастер-узел, убивает процесс мастер-узла, после этого заканчивает изменения данных и проверяет целостность данных.&lt;br /&gt;
&lt;br /&gt;
=== Защита лабораторной работы ===&lt;br /&gt;
* Показать и выполнить запросы с агрегацией, объяснить структуру запросов&lt;br /&gt;
* Продемонстрировать работу скрипта инициализации сбоя во время работы и объяснить, что происходит с репликами MongoDB&lt;/div&gt;</summary>
		<author><name>ADKosm</name></author>
	</entry>
	<entry>
		<id>https://wiki.cs.hse.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=19655</id>
		<title>Базы данных/Лабораторная работа 4</title>
		<link rel="alternate" type="text/html" href="https://wiki.cs.hse.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=19655"/>
		<updated>2016-06-07T18:42:02Z</updated>

		<summary type="html">&lt;p&gt;ADKosm: /* Создание таблицы истории */&lt;/p&gt;
&lt;hr /&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 &#039;The phonetic code for % is %&#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(&#039;How I will pass all the exams&#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 &#039;%&#039;||movie_name||&#039;%&#039;;&lt;br /&gt;
    EXCEPTION&lt;br /&gt;
        WHEN NO_DATA_FOUND THEN&lt;br /&gt;
            RAISE EXCEPTION &#039;movie % not found&#039;, movie_name;&lt;br /&gt;
        WHEN TOO_MANY_ROWS THEN&lt;br /&gt;
            RAISE EXCEPTION &#039;movie % not unique&#039;, movie_name;&lt;br /&gt;
  END;&lt;br /&gt;
&lt;br /&gt;
В этом примере также есть конструкция title like &#039;%&#039;||movie_name||&#039;%&#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 &#039;Currently working on movie %s ...&#039;, quote_ident(movie_row.title);&lt;br /&gt;
        IF movie_row.production_year &amp;gt; 2000 THEN&lt;br /&gt;
            RAISE NOTICE &#039;This movie is of this century&#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 &#039;Data processing...&#039;&lt;br /&gt;
&lt;br /&gt;
Команда завершающая процедуру или транзакцию с ошибкой:&lt;br /&gt;
&lt;br /&gt;
  RAISE EXCEPTION &#039;Something went wrong&#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 = &#039;public&#039; and table_name=replace(relname, &#039;_id_seq&#039;, &amp;amp;apos;&amp;amp;apos; )&lt;br /&gt;
    where s.relkind=&#039;S&#039;) LOOP&lt;br /&gt;
      EXECUTE &#039;select max(id) from &#039;||t.table_name into max_id; -- выполняет запрос, сформированный в строке и передает результат в max_id&lt;br /&gt;
      raise notice &#039;max id for % is %&#039;, t.table_name, max_id;&lt;br /&gt;
      if max_id is not null then&lt;br /&gt;
         -- выполняет запрос увеличения текущего значения последовательности&lt;br /&gt;
         EXECUTE &#039;SELECT setval(&amp;amp;apos;&amp;amp;apos;&#039;||t.table_name||&#039;_id_seq&#039;&amp;amp;apos;, &#039;||max_id||&#039;, true)&#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 &#039;Invalid title&#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>ADKosm</name></author>
	</entry>
	<entry>
		<id>https://wiki.cs.hse.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=19654</id>
		<title>Базы данных/Лабораторная работа 4</title>
		<link rel="alternate" type="text/html" href="https://wiki.cs.hse.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=19654"/>
		<updated>2016-06-07T16:39:02Z</updated>

		<summary type="html">&lt;p&gt;ADKosm: /* Определение переменных */&lt;/p&gt;
&lt;hr /&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 &#039;The phonetic code for % is %&#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(&#039;How I will pass all the exams&#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 &#039;%&#039;||movie_name||&#039;%&#039;;&lt;br /&gt;
    EXCEPTION&lt;br /&gt;
        WHEN NO_DATA_FOUND THEN&lt;br /&gt;
            RAISE EXCEPTION &#039;movie % not found&#039;, movie_name;&lt;br /&gt;
        WHEN TOO_MANY_ROWS THEN&lt;br /&gt;
            RAISE EXCEPTION &#039;movie % not unique&#039;, movie_name;&lt;br /&gt;
  END;&lt;br /&gt;
&lt;br /&gt;
В этом примере также есть конструкция title like &#039;%&#039;||movie_name||&#039;%&#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 &#039;Currently working on movie %s ...&#039;, quote_ident(movie_row.title);&lt;br /&gt;
        IF movie_row.production_year &amp;gt; 2000 THEN&lt;br /&gt;
            RAISE NOTICE &#039;This movie is of this century&#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 &#039;Data processing...&#039;&lt;br /&gt;
&lt;br /&gt;
Команда завершающая процедуру или транзакцию с ошибкой:&lt;br /&gt;
&lt;br /&gt;
  RAISE EXCEPTION &#039;Something went wrong&#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 = &#039;public&#039; and table_name=replace(relname, &#039;_id_seq&#039;, &amp;amp;apos;&amp;amp;apos; )&lt;br /&gt;
    where s.relkind=&#039;S&#039;) LOOP&lt;br /&gt;
      EXECUTE &#039;select max(id) from &#039;||t.table_name into max_id; -- выполняет запрос, сформированный в строке и передает результат в max_id&lt;br /&gt;
      raise notice &#039;max id for % is %&#039;, t.table_name, max_id;&lt;br /&gt;
      if max_id is not null then&lt;br /&gt;
         -- выполняет запрос увеличения текущего значения последовательности&lt;br /&gt;
         EXECUTE &#039;SELECT setval(&amp;amp;apos;&amp;amp;apos;&#039;||t.table_name||&#039;_id_seq&#039;&amp;amp;apos;, &#039;||max_id||&#039;, true)&#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 &#039;Invalid title&#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>ADKosm</name></author>
	</entry>
	<entry>
		<id>https://wiki.cs.hse.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=19653</id>
		<title>Базы данных/Лабораторная работа 4</title>
		<link rel="alternate" type="text/html" href="https://wiki.cs.hse.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=19653"/>
		<updated>2016-06-07T16:19:53Z</updated>

		<summary type="html">&lt;p&gt;ADKosm: ALTER SEQUENCE  INCREMENT изменяет значение параметра increment_by, а не last_value. Не отображались кавычки в последнем аргументе replace. Не нужно добавлять 1 к max(id)&lt;/p&gt;
&lt;hr /&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 &#039;The phonetic code for % is %&#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(&#039;How I will pass all the exams&#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 =: 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 &#039;%&#039;||movie_name||&#039;%&#039;;&lt;br /&gt;
    EXCEPTION&lt;br /&gt;
        WHEN NO_DATA_FOUND THEN&lt;br /&gt;
            RAISE EXCEPTION &#039;movie % not found&#039;, movie_name;&lt;br /&gt;
        WHEN TOO_MANY_ROWS THEN&lt;br /&gt;
            RAISE EXCEPTION &#039;movie % not unique&#039;, movie_name;&lt;br /&gt;
  END;&lt;br /&gt;
&lt;br /&gt;
В этом примере также есть конструкция title like &#039;%&#039;||movie_name||&#039;%&#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 &#039;Currently working on movie %s ...&#039;, quote_ident(movie_row.title);&lt;br /&gt;
        IF movie_row.production_year &amp;gt; 2000 THEN&lt;br /&gt;
            RAISE NOTICE &#039;This movie is of this century&#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 &#039;Data processing...&#039;&lt;br /&gt;
&lt;br /&gt;
Команда завершающая процедуру или транзакцию с ошибкой:&lt;br /&gt;
&lt;br /&gt;
  RAISE EXCEPTION &#039;Something went wrong&#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 = &#039;public&#039; and table_name=replace(relname, &#039;_id_seq&#039;, &amp;amp;apos;&amp;amp;apos; )&lt;br /&gt;
    where s.relkind=&#039;S&#039;) LOOP&lt;br /&gt;
      EXECUTE &#039;select max(id) from &#039;||t.table_name into max_id; -- выполняет запрос, сформированный в строке и передает результат в max_id&lt;br /&gt;
      raise notice &#039;max id for % is %&#039;, t.table_name, max_id;&lt;br /&gt;
      if max_id is not null then&lt;br /&gt;
         -- выполняет запрос увеличения текущего значения последовательности&lt;br /&gt;
         EXECUTE &#039;SELECT setval(&amp;amp;apos;&amp;amp;apos;&#039;||t.table_name||&#039;_id_seq&#039;&amp;amp;apos;, &#039;||max_id||&#039;, true)&#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 &#039;Invalid title&#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>ADKosm</name></author>
	</entry>
</feed>