\documentclass[a4paper, final]{article}
%\usepackage{literat} % Нормальные шрифты
\usepackage[14pt]{extsizes} % для того чтобы задать нестандартный 14-ый размер шрифта
\usepackage{tabularx}
\usepackage[T2A]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[russian]{babel}
\usepackage{amsmath}
\usepackage[left=25mm, top=20mm, right=20mm, bottom=20mm, footskip=10mm]{geometry}
\usepackage{ragged2e} %для растягивания по ширине
\usepackage{setspace} %для межстрочного интервала
\usepackage{moreverb} %для работы с листингами
\usepackage{indentfirst} % для абзацного отступа
\usepackage{moreverb} %для печати в листинге исходного кода программ
\usepackage{pdfpages} %для вставки других pdf файлов
\usepackage{tikz}
\usepackage{graphicx}
\usepackage{afterpage}
\usepackage{longtable}
\usepackage{float}



% \usepackage[paper=A4,DIV=12]{typearea}
\usepackage{pdflscape}
% \usepackage{lscape}

\usepackage{array}
\usepackage{multirow}

\renewcommand\verbatimtabsize{4\relax}
\renewcommand\listingoffset{0.2em} %отступ от номеров строк в листинге
\renewcommand{\arraystretch}{1.4} % изменяю высоту строки в таблице
\usepackage[font=small, singlelinecheck=false, justification=centering, format=plain, labelsep=period]{caption} %для настройки заголовка таблицы
\usepackage{listings} %листинги
\usepackage{xcolor} % цвета
\usepackage{hyperref}% для гиперссылок
\usepackage{enumitem} %для перечислений

% Настраиваем листинги, чтобы они использовали счётчик figure
\AtBeginDocument{
  \renewcommand{\thelstlisting}{\thefigure}  % Листинги используют тот же счетчик, что и рисунки
  \renewcommand{\lstlistingname}{Рис.}    % Меняем подпись на "Рисунок"
}

% Автоматически увеличиваем счетчик figure перед каждым листингом
\let\oldlstlisting\lstlisting
\renewcommand{\lstlisting}[1][]{%
    \refstepcounter{figure}% Увеличиваем счетчик figure
    \oldlstlisting[#1]% Вызываем оригинальную команду lstlisting
}

\newcommand{\specialcell}[2][l]{\begin{tabular}[#1]{@{}l@{}}#2\end{tabular}}


\setlist[enumerate,itemize]{leftmargin=1.2cm} %отступ в перечислениях

\hypersetup{colorlinks,
    allcolors=[RGB]{010 090 200}} %красивые гиперссылки (не красные)

% подгружаемые языки — подробнее в документации listings (это всё для листингов)
\lstloadlanguages{ SQL}
% включаем кириллицу и добавляем кое−какие опции
\lstset{tabsize=2,
    breaklines,
    basicstyle=\footnotesize,
    columns=fullflexible,
    flexiblecolumns,
    numbers=left,
    numberstyle={\footnotesize},
    keywordstyle=\color{blue},
    inputencoding=cp1251,
    extendedchars=true
}
\lstdefinelanguage{MyC}{
    language=SQL,
%    ndkeywordstyle=\color{darkgray}\bfseries,
%    identifierstyle=\color{black},
%    morecomment=[n]{/**}{*/},
%    commentstyle=\color{blue}\ttfamily,
%    stringstyle=\color{red}\ttfamily,
%    morestring=[b]",
%    showstringspaces=false,
%    morecomment=[l][\color{gray}]{//},
    keepspaces=true,
    escapechar=\%,
    texcl=true
}

\textheight=24cm % высота текста
\textwidth=16cm % ширина текста
\oddsidemargin=0pt % отступ от левого края
\topmargin=-1.5cm % отступ от верхнего края
\parindent=24pt % абзацный отступ
\parskip=5pt % интервал между абзацами
\tolerance=2000 % терпимость к "жидким" строкам
\flushbottom % выравнивание высоты страниц


% Настройка листингов
\lstset{
    language=SQL,
    extendedchars=\true,
    inputencoding=utf8,
    keepspaces=true,
    captionpos=b,
}

\begin{document} % начало документа
    


    % НАЧАЛО ТИТУЛЬНОГО ЛИСТА
    \begin{center}
        \hfill \break
        \hfill \break
        \normalsize{МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ\\
            федеральное государственное автономное образовательное учреждение высшего образования «Санкт-Петербургский политехнический университет Петра Великого»\\[10pt]}
        \normalsize{Институт компьютерных наук и кибербезопасности}\\[10pt] 
        \normalsize{Высшая школа технологий искусственного интеллекта}\\[10pt] 
        \normalsize{Направление: 02.03.01 <<Математика и компьютерные науки>>}\\
        
        \hfill \break
        \hfill \break
        \hfill \break
        \hfill \break
        \large{Отчет по лабораторным работам}\\
        \large{<<Теоретические основы баз данных>>}\\
        \hfill \break
        
        % \hfill \break
        \hfill \break
    \end{center}
    
    \small{ 
        \begin{tabular}{lrrl}
            \!\!\!Студент, & \hspace{2cm} & & \\
            \!\!\!группы 5130201/20102 & \hspace{2cm} & \underline{\hspace{3cm}} &Тищенко А. А. \\\\
            \!\!\!Преподаватель & \hspace{2cm} &  \underline{\hspace{3cm}} &  Попов С. Г. \\\\
            &&\hspace{4cm}
        \end{tabular}
        \begin{flushright}
            <<\underline{\hspace{1cm}}>>\underline{\hspace{2.5cm}} 2024г.
        \end{flushright}
    }
    
    \hfill \break
    % \hfill \break
    \begin{center} \small{Санкт-Петербург, 2024} \end{center}
    \thispagestyle{empty} % выключаем отображение номера для этой страницы
    
    % КОНЕЦ ТИТУЛЬНОГО ЛИСТА
    \newpage
    
    \tableofcontents


    \newpage
    
    \section*{Введение}
    
    \addcontentsline{toc}{section}{Введение}
    В данном отчёте описан результат выполнения лабораторных работ, расширяющих функциональные возможности базы данных для проведения соревнований по стрельбе из лука, которая была разработана в течение предыдущего семестрового курса «Теоретические основы баз данных».
    В ходе выполнения лабораторных работ изучены и реализованы в СУБД: представления, событийная модель и триггеры, разграничение прав доступа пользователей.
    % процедуры, функции и транзакции

    \newpage
    
    \section {Постановка задачи}
    В ходе прохождения данного курса необходимо выполнить пять лабораторных работ.

    \begin{enumerate}
        \item Создать представление, инкапсулирующее запрос. Написать запрос, использующий в себе представление.
        \item Написать триггеры, автоматизирующие сбор статистической информации о количестве соревнований, в которых участвовал каждый судья.
        \item Создать двух пользователей. Первый должен иметь доступ только на просмотр представления из первого задания. Второй также должен уметь редактировать таблицы, участвующие в запросе представления.
    \end{enumerate}

    \newpage
    \section {Лабораторные работы}
    \subsection{Работа 1: Создание представлений}
    \textbf{Задача:} Разработать представление для хранения запроса внутри СУБД.

    \textbf{Формулировка задачи:} Посчитать для каждого спортсмена число заявок и число серий. Представление выдает только идентификатор спортсмена, число его заявок на соревнования и число серий выстрелов. Использовать представление в другом запросе.
    
    Было создано представление sportsman\_requests\_series\_count, которое хранит запрос, выполняющий поставленную задачу. В представлении участвуют таблицы \texttt{sportsman}, \texttt{participant\_request} и \texttt{shot\_series}. Код запроса для создания представления представлен на Рис.~\ref{lst:view1}, а первые десять записей получившегося представления представлены на Рис.~\ref{fig:view}.

    \begin{figure}[h]
        \centering
        \includegraphics[width=0.4\linewidth]{img/view_result.png}
        \caption{Первые десять записей в представлении \texttt{sportsman\_requests\_series\_count}.}
        \label{fig:view}
    \end{figure}

    \begin{lstlisting}[language=SQL, caption={Запрос для создания представления \texttt{sportsman\_requests\_series\_count}.}, label={lst:view1}]
    create view
        sportsman_requests_series_count as
    select
        s.id_sportsman,
        count(distinct pr.id_participant_request) as pr_count,
        count(ss.id_shot_series) as ss_count
    from
        sportsman as s
        left join participant_request as pr 
            on s.id_sportsman = pr.id_sportsman
        left join shot_series as ss 
            on pr.id_participant_request = ss.id_participant_request
    group by
        s.id_sportsman;
\end{lstlisting}

    Также был составлен пример запроса, в котором используется представление \texttt{sportsman\_requests\_series\_count}, его код представлен на Рис.~\ref{lst:view2}. В этом запросе выбираются 10 самых активных с точки зрения участия в соревнованиях спортсменов из клуба <<ЛК Парадокс Лучника>>. К исходному представлению также добавляются дополнительные столбцы с данными спортсмена из таблицы \texttt{sportsman}. Результат выполнения этого запроса представлен на Рис.~\ref{fig:view_query}.

    \begin{lstlisting}[language=SQL, caption={Код примера запроса, в котором используется представление \texttt{sportsman\_requests\_series\_count}.}, label={lst:view2}]
    select
        srsc.id_sportsman,
        s.name,
        s.category,
        srsc.pr_count,
        srsc.ss_count
    from
        sportsman_requests_series_count as srsc
        join sportsman as s on srsc.id_sportsman = s.id_sportsman
    where
        s.gender = 'Мужчина'
        and s.club = 'ЛК Парадокс Лучника'
    order by
        srsc.pr_count desc
    limit 10;
\end{lstlisting}

    \begin{figure}[h]
        \centering
        \includegraphics[width=1\linewidth]{img/view_query.png}
        \caption{Результат выполнения запроса с участием представления \texttt{sportsman\_requests\_series\_count}.}
        \label{fig:view_query}
    \end{figure}
    
    Созданное представление нельзя обновлять напрямую, а попытки его обновить приведут к ошибке, которая демонстрируется на Рис.~\ref{fig:insert_error}. Изменить данные представления можно только изменив исходные таблицы, которые в нём участвуют. 

    \begin{figure}[h]
        \centering
        \includegraphics[width=1\linewidth]{img/insert_error.png}
        \caption{Результат попытки изменения данных напрямую через представление \texttt{sportsman\_requests\_series\_count}.}
        \label{fig:insert_error}
    \end{figure}


    \subsection{Работа 2: Событийная модель, триггеры}
    \textbf{Задача:} Разработать триггер, производящий манипуляции над БД при добавлении, удалении и обновлении данных.

    \textbf{Формулировка задачи:} Необходимо вести статистику о том, в скольки соревнованиях участвовал каждый судья. Событийная модель должна поддерживать актуальность данных в таблицах со статистикой при изменении данных.
    
    Для выполнения поставленной задачи, была создана отдельная таблица для сохранения статистики -- \texttt{judge\_statistics}. Код запроса для её создания представлен на Рис.~\ref{lst:trigger-table}.

    \begin{lstlisting}[language=SQL, caption={Код запроса для создания таблицы \texttt{judge\_statistics}.}, label={lst:trigger-table}]
    create table judge_statistics (
        id_judge integer primary key,
        competition_count integer not null default 0,
        foreign key (id_judge) references judge(id_judge)
    );
\end{lstlisting}

    Данные в таблице \texttt{judge\_statistics} должны изменяться при изменении таблиц \texttt{judge} и \texttt{judge\_request}. Список созданных для этого триггеров представлен ниже.

    \begin{enumerate}
        \item \texttt{after\_judge\_insert}
            \begin{itemize}
                \item \textbf{Срабатывает при} добавлении новых судей в таблицу \texttt{judge}.
                \item \textbf{Действие:} добавляет нового судью в \texttt{judge\_statistics}.
                \item \textbf{Код:} листинг~\ref{lst:trigger1}.
            \end{itemize}
\begin{lstlisting}[language=SQL, caption={Код запроса для создания триггера \texttt{after\_judge\_insert} и функции \texttt{update\_statistics\_on\_judge\_insert}.}, label={lst:trigger1}]
create or replace function update_statistics_on_judge_insert()
returns trigger as $$
begin
    insert into judge_statistics (id_judge, competition_count)
    values (new.id_judge, 0);
    return new;
end;
$$ language plpgsql;

create trigger after_judge_insert
after insert on judge
for each row
execute function update_statistics_on_judge_insert();
\end{lstlisting}
        \item \texttt{after\_judge\_delete}
            \begin{itemize}
                \item \textbf{Срабатывает при} удалении судей из таблицы \texttt{judge}.
                \item \textbf{Действие:} удаляет судей из \texttt{judge\_statistics}.
                \item \textbf{Код:} листинг~\ref{lst:trigger2}.
            \end{itemize}
\begin{lstlisting}[language=SQL, caption={Код запроса для создания триггера \texttt{after\_judge\_delete} и функции \texttt{update\_statistics\_on\_judge\_delete}.}, label={lst:trigger2}]
create or replace function update_statistics_on_judge_delete()
returns trigger as $$
begin
    delete from judge_statistics where id_judge = old.id_judge;
    return old;
end;
$$ language plpgsql;

create trigger after_judge_delete
after delete on judge
for each row
execute function update_statistics_on_judge_delete();
\end{lstlisting}
        \item \texttt{after\_judge\_request\_insert}
            \begin{itemize}
                \item \textbf{Срабатывает при} добавлении заявки судьи на участие в соревновании в таблицу \texttt{judge\_request}.
                \item \textbf{Действие:} увеличивает на единицу счётчик с количеством соревнований \\ в \texttt{judge\_statistics} для судьи, от имени которого была добавлена заявка.
                \item \textbf{Код:} листинг~\ref{lst:trigger3}.
            \end{itemize}
\begin{lstlisting}[language=SQL, caption={Код запроса для создания триггера \texttt{after\_judge\_request\_insert} и функции \texttt{update\_statistics\_on\_judge\_request\_insert}.}, label={lst:trigger3}]
create or replace function update_statistics_on_judge_request_insert()
returns trigger as $$
begin
    update judge_statistics
    set competition_count = competition_count + 1
    where id_judge = new.id_judge;
    return new;
end;
$$ language plpgsql;

create trigger after_judge_request_insert
after insert on judge_request
for each row
execute function update_statistics_on_judge_request_insert();
\end{lstlisting}
    \item \texttt{after\_judge\_request\_delete}
    \begin{itemize}
        \item \textbf{Срабатывает при} удалении заявки судьи на участие в соревновании из таблицы \texttt{judge\_request}.
        \item \textbf{Действие:} уменьшает на единицу счётчик с количеством соревнований \\ в \texttt{judge\_statistics} для судьи, от имени которого была добавлена заявка.
        \item \textbf{Код:} листинг~\ref{lst:trigger4}.
    \end{itemize}
\begin{lstlisting}[language=SQL, caption={Код запроса для создания триггера \texttt{after\_judge\_request\_delete} и функции \texttt{update\_statistics\_on\_judge\_request\_delete}.}, label={lst:trigger4}]
create or replace function update_statistics_on_judge_request_delete()
returns trigger as $$
begin
    update judge_statistics
    set competition_count = competition_count - 1
    where id_judge = old.id_judge;
    return old;
end;
$$ language plpgsql;

create trigger after_judge_request_delete
after delete on judge_request
for each row
execute function update_statistics_on_judge_request_delete();
\end{lstlisting}
\item \texttt{after\_judge\_request\_update}
\begin{itemize}
    \item \textbf{Срабатывает при} изменении заявки судьи на участие в соревновании в таблице \texttt{judge\_request}.
    \item \textbf{Действие:} уменьшает на единицу счётчик с количеством соревнований для \\ в \texttt{judge\_statistics} для судьи, от имени которого была добавлена заявка.
    \item \textbf{Код:} листинг~\ref{lst:trigger5}.
\end{itemize}
\begin{lstlisting}[language=SQL, caption={Код запроса для создания триггера \texttt{after\_judge\_request\_update} и функции \texttt{update\_statistics\_on\_judge\_request\_update}.}, label={lst:trigger5}]
create or replace function update_statistics_on_judge_request_update()
returns trigger as $$
begin
    -- уменьшение счётчика у старого судьи
    update judge_statistics
    set competition_count = competition_count - 1
    where id_judge = old.id_judge;

    -- увеличение счётчика у нового судьи
    update judge_statistics
    set competition_count = competition_count + 1
    where id_judge = new.id_judge;

    return new;
end;
$$ language plpgsql;

create trigger after_judge_request_update
after update on judge_request
for each row
execute function update_statistics_on_judge_request_update();
\end{lstlisting}
\end{enumerate}

После создания таблицы \texttt{judge\_statistics} и всех триггеров, база данных была заполнена заново с помощью скрипта, разработанного ещё в прошлом семестре, для того, чтобы таблица содержала актуальные данные. Первые десять строк таблицы \texttt{judge\_statistics} демонстрируются на Рис.~\ref{fig:judge_stat}.

\begin{figure}[h]
   \centering
   \includegraphics[width=0.35\linewidth]{img/judge_stat.png}
   \caption{Первые десять записей в таблице \texttt{judge\_statistics}.}
   \label{fig:judge_stat}
\end{figure}


\subsection{Работа 3: Разграничение прав доступа}
\textbf{Задача:} Создать пользователей и предоставить им различные права доступа к представлению и таблицам, которые в нём участвуют.

\textbf{Формулировка задачи:} Создать пользователя \texttt{readonly\_user}  и \texttt{edit\_user}. Выдать первому права только на чтение данных из представления, созданного ранее. Второму также выдать права на редактирование всех таблиц, участвующих в этом представлении. Сравнить результаты различных операций с этими таблицам от имени этих пользователей.

Код запросов для создания пользователей и выдачи им необходимых прав представлен на Рис.~\ref{lst:users}. По мимо стандартных прав на операции \texttt{insert}, \texttt{update}, \texttt{delete}, пользователю \texttt{readonly\_user} также необходимо предоставить права \texttt{usage} и \texttt{select} для объектов последовательностей, связанных с редактируемыми таблицами. Это особенность СУБД PostgreSQL, в которых последовательности выделены в отдельные объекты базы данных и, соответственно, для работы с ними необходимо явно предоставить пользователю права. Последовательности используются в PostgreSQL для автоинкрементных полей, в первую очередь для первичных ключей таблиц. Также обоим пользователем необходимо предоставить права на использование схемы базы данных, без этого разрешения пользователь даже при наличии прав на отдельные объекты внутри схемы не сможет к ним получить доступ.

Примеры различных операций от имён этих пользователей вместе с реакцией СУБД демонстрируются в таблице~\ref{tbl:users}.

\begin{lstlisting}[caption={Код запросов для создания пользователей и выдачи им определённых прав.}, label={lst:users}]
create user readonly_user with password '123';
grant usage on schema public to readonly_user;
grant select on sportsman_requests_series_count to readonly_user;

create user edit_user with password '123';
grant usage on schema public to edit_user;
grant select on sportsman_requests_series_count to edit_user;
grant select, insert, update, delete on sportsman to edit_user;
grant select, insert, update, delete on participant_request to edit_user;
grant select, insert, update, delete on shot_series to edit_user;
grant usage, select on sequence sportsman_id_sportsman_seq to edit_user;
grant usage, select on sequence participant_request_id_participant_request_seq to edit_user;
grant usage, select on sequence shot_series_id_shot_series_seq to edit_user;
\end{lstlisting}


\begin{table}[h!]
    \centering
    \caption{Сравнение результатов действий, выполненных от пользователей \texttt{readonly\_user} и \texttt{edit\_user}.}
    \label{tbl:users}
    \footnotesize
    \begin{tabularx}{\textwidth}{|c|X|X|}
    \hline
    \textbf{№} & \textbf{edit\_user} & \textbf{readonly\_user} \\
    \hline

    
    \multirow{3}{*}{1} & \multicolumn{2}{c|}{Просмотр содержимого представления. }\\
    % \cline{2-3}
    & \multicolumn{2}{c|}{\specialcell{\texttt{select * from sportsman\_requests\_series\_count}}}\\
    \cline{2-3}
    & 
    \vspace{-11pt}
    \hspace{-8.5pt}
    \includegraphics[width=1\linewidth]{img/try1_edit_user.png}
    & 
    \vspace{-11pt}
    \hspace{-8.5pt}
    \includegraphics[width=1\linewidth]{img/try1_edit_user.png}
    \\
    \hline

    \multirow{3}{*}{2} & \multicolumn{2}{c|}{Изменение данных через представление. }\\
    % \cline{2-3}
    & \multicolumn{2}{c|}{\specialcell{\texttt{insert into sportsman\_requests\_series\_count (id\_sportsman, pr\_count, ss\_count)}\\ \texttt{values (100, 50, 25)}}}\\
    \cline{2-3}
    & 
    \vspace{-11pt}
    \hspace{-8.5pt}
    \includegraphics[width=1\linewidth]{img/try2.png}
    & 
    \vspace{-11pt}
    \hspace{-8.5pt}
    \includegraphics[width=1\linewidth]{img/try2.png}
    \\
    \hline

    \end{tabularx}
\end{table}


\begin{table}[h!]
    \centering
    \addtocounter{table}{-1}
    \caption{Сравнение результатов действий, выполненных от пользователей \texttt{readonly\_user} и \texttt{edit\_user} (продолжение).}
    \footnotesize
    \begin{tabularx}{\textwidth}{|c|X|X|}
    \hline
    \textbf{№} & \textbf{edit\_user} & \textbf{readonly\_user} \\
    \hline

    \multirow{3}{*}{3} & \multicolumn{2}{c|}{Чтение данных из таблицы, несвязанной с представлением. }\\
    % \cline{2-3}
    & \multicolumn{2}{c|}{\specialcell{\texttt{select * from judge}}}\\
    \cline{2-3}
    & 
    \vspace{-11pt}
    \hspace{-8.5pt}
    \includegraphics[width=1\linewidth]{img/try3.png}
    & 
    \vspace{-11pt}
    \hspace{-8.5pt}
    \includegraphics[width=1\linewidth]{img/try3.png}
    \\
    \hline

    \multirow{3}{*}{4} & \multicolumn{2}{c|}{Чтение данных из таблицы \texttt{sportsman}. }\\
    % \cline{2-3}
    & \multicolumn{2}{c|}{\specialcell{\texttt{select * from sportsman}}}\\
    \cline{2-3}
    & 
    \vspace{-11pt}
    \hspace{-8.5pt}
    \includegraphics[width=1\linewidth]{img/try4_edit_user.png}
    & 
    \vspace{-11pt}
    \hspace{-8.5pt}
    \includegraphics[width=1\linewidth]{img/try4_readonly_user.png}
    \\
    \hline

    \multirow{3}{*}{5} & \multicolumn{2}{c|}{Чтение данных из таблицы \texttt{participant\_request}. }\\
    % \cline{2-3}
    & \multicolumn{2}{c|}{\specialcell{\texttt{select * from participant\_request}}}\\
    \cline{2-3}
    & 
    \vspace{-11pt}
    \hspace{-8.5pt}
    \includegraphics[width=1\linewidth]{img/try5_edit_user.png}
    & 
    \vspace{-11pt}
    \hspace{-8.5pt}
    \includegraphics[width=1\linewidth]{img/try5_readonly_user.png}
    \\
    \hline

    \multirow{3}{*}{6} & \multicolumn{2}{c|}{Чтение данных из таблицы \texttt{shot\_series}. }\\
    % \cline{2-3}
    & \multicolumn{2}{c|}{\specialcell{\texttt{select * from shot\_series}}}\\
    \cline{2-3}
    & 
    \vspace{-11pt}
    \hspace{-8.5pt}
    \includegraphics[width=1\linewidth]{img/try6_edit_user.png}
    & 
    \vspace{-11pt}
    \hspace{-8.5pt}
    \includegraphics[width=1\linewidth]{img/try6_readonly_user.png}
    \\
    \hline

    \multirow{3}{*}{7} & \multicolumn{2}{c|}{Добавление данных в таблицу \texttt{participant\_request}. }\\
    % \cline{2-3}
    & \multicolumn{2}{c|}{\specialcell{\texttt{insert into shot\_series (score, photo, id\_participant\_request,}\\ \texttt{id\_result\_in\_stage, id\_judge\_request)}\\ \texttt{values (10, 'path/to/photo.jpg', 16784, 67368, 3403)}}}\\
    \cline{2-3}
    & 
    \vspace{-11pt}
    \hspace{-8.5pt}
    \includegraphics[width=1\linewidth]{img/try7_edit_user.png}
    & 
    \vspace{-11pt}
    \hspace{-8.5pt}
    \includegraphics[width=1\linewidth]{img/try7_readonly_user.png}
    \\
    \hline

    \multirow{3}{*}{8} & \multicolumn{2}{c|}{Добавление данных в таблицу, несвязанную с представлением. }\\
    % \cline{2-3}
    & \multicolumn{2}{c|}{\specialcell{\texttt{insert into judge (name, category, region, federation)}\\ \texttt{values ('Иван Иванов', '1 категория', 'Санкт-Петербург', 'Федерация')}}}\\
    \cline{2-3}
    & 
    \vspace{-11pt}
    \hspace{-8.5pt}
    \includegraphics[width=1\linewidth]{img/try8.png}
    & 
    \vspace{-11pt}
    \hspace{-8.5pt}
    \includegraphics[width=1\linewidth]{img/try8.png}
    \\
    \hline

    \end{tabularx}
\end{table}


\subsection{Работа 4: Создание функций и процедур}
\textbf{Задача:} Создать функцию и использовать её в запросе. Создать процедуру, которая будет создавать новые записи в таблицах по определённым условиям.

\textbf{Формулировка задачи:} Создать функцию \texttt{convert\_to\_initials}, которая будет получать на вход строки с ФИО, а возвращать строку с инициалами. Функция должна корректно обрабатывать случай, когда отчество отсутствует. Создать процедуру \texttt{create\_participant\_request}, которая по заданной информации о соревновании, спортсмене и дивизионе будет создавать заявку на участие в соревновании.

Код определения функции \texttt{convert\_to\_initials} представлен на Рис.~\ref{lst:convert_to_initials}. Функция принимает на вход три параметра типа \texttt{varchar}: \texttt{p\_name} -- имя, \texttt{p\_surname} -- фамилия, \texttt{p\_patronymic} -- отчество. Возвращает также \texttt{varchar} -- строку с инициалами. Функция отдельно обрабатывает случай, когда не задано отчество, в таком случае инициалы будут состоять только из первой буквы имени и фамилии. Код запроса с использованием этой функции представлен на Рис.~\ref{lst:convert_query}, а результат его выполнения на Рис.~\ref{fig:conver_result}.


\begin{lstlisting}[caption={Код определения функции \texttt{convert\_to\_initials}.}, label={lst:convert_to_initials}]
create or replace function convert_to_initials(
    p_name varchar,
    p_surname varchar,
    p_patronymic varchar
) returns varchar as $$
begin
    if p_patronymic is null or p_patronymic = '' then
        return concat(substring(p_name from 1 for 1), '. ', p_surname);
    end if;

    return concat(substring(p_patronymic from 1 for 1), '. ', substring(p_name from 1 for 1), '. ', p_surname);
end;
$$ language plpgsql;
\end{lstlisting}

\begin{lstlisting}[caption={Код запроса с использованием функции \texttt{convert\_to\_initials}.}, label={lst:convert_query}]
select
    id_judge,
    convert_to_initials (name, surname, patronymic),
    name,
    surname,
    patronymic
from
    judge
\end{lstlisting}

\begin{figure}[h]
   \centering
   \includegraphics[width=1\linewidth]{img/conver_result.png}
   \caption{Первые десять записей результата выполнения запроса с применением функции \texttt{convert\_to\_initials}.}
   \label{fig:conver_result}
\end{figure}

Код определения процедуры \texttt{create\_participant\_request} представлен на Рис.~\ref{lst:procedure}. Процедура принимает на вход пять параметров типа \texttt{varchar}: \texttt{p\_name} -- имя спортсмена, \texttt{p\_surname} -- фамилия спортсмена, \texttt{p\_gender} -- пол спортсмена, \texttt{p\_competition\_title} -- название соревнования, \texttt{p\_division\_title} -- название дивизиона. Внутри процедуры сначала проверяется существование дивизиона в базе данных по переданному названию. Если дивизион не найден, выбрасывается ошибка. Затем проверяется существование соревнования. Если соревнование не существует, оно создаётся. Далее проверяется, существует ли спортсмен с указанными именем, фамилией и полом. Если спортсмен не найден, то создаётся новая запись о спортсмене. В конце создаётся заявка участника с указанием данных спортсмена, соревнования и дивизиона.

\begin{lstlisting}[caption={Код определения процедуры \texttt{create\_participant\_request}.}, label={lst:procedure}]
create or replace procedure create_participant_request(
    p_name varchar(100),
    p_surname varchar(100),
    p_gender varchar(10),
    p_competition_title varchar(100),
    p_division_title varchar(100)
)
language plpgsql
as $$
declare
    v_id_sportsman integer;
    v_id_competition integer;
    v_id_division integer;
begin
    -- проверка существования дивизиона
    select id_division into v_id_division
    from division
    where title = p_division_title
    limit 1;

    if not found then
        raise exception 'Дивизиона % не существует', p_division_title;
    end if;

    -- проверка существования соревнования
    select id_competition into v_id_competition
    from competition
    where title = p_competition_title
    limit 1;

    -- если соревнование не существует, создать его
    if not found then
        insert into competition (title)
        values (p_competition_title)
        returning id_competition into v_id_competition;
    end if;

    -- проверка существования спортсмена
    select id_sportsman into v_id_sportsman
    from sportsman
    where name = p_name and surname = p_surname and gender = p_gender
    limit 1;

    -- если спортсмен не существует, создать его
    if not found then
        insert into sportsman (name, surname, gender)
        values (p_name, p_surname, p_gender)
        returning id_sportsman into v_id_sportsman;
    end if;

    -- создание заявки участника
    insert into participant_request (name, surname, gender, is_registered, id_competition, id_sportsman, id_division)
    values (p_name, p_surname, p_gender, false, v_id_competition, v_id_sportsman, v_id_division);
end;
$$;
\end{lstlisting}


\subsection{Работа 5: Транзакционная модель}
\textbf{Задача:} Выбрать определённый уровень изоляции транзакций и продемонстрировать наличие или отсутствие артефактов: <<Грязное чтение>>, <<Неповторяемое чтение>> и <<Фантомы>>.

Уровень изоляции \texttt{Read Commited} в PostgreSQL задётся при помощи команды:

\texttt{SET TRANSACTION ISOLATION LEVEL READ COMMITTED;}

На этом уровне изоляции исключается артефакт <<Грязное чтение>>, однако сохраняются артефакты <<Неповторяемое чтение>> и <<Фантомы>>.

В таблице~\ref{tbl:bad_read} представлены транзакции, на примере которых демонстрируется отсутствие артефакта <<Грязное чтение>>.

\newpage
\begin{table}[h!]
    \centering
    % \addtocounter{table}{-1}
    \caption{Транзакции для демонстрации отсутствия артефакта <<Грязное чтение>>.}
    \label{tbl:bad_read}
    \footnotesize
    \begin{tabularx}{\textwidth}{|c|X|X|}
    \hline
    t & Транзакция 1 & Транзакция 2 \\
    \hline

    \multirow{2}{*}{} 
    & \multicolumn{2}{c|}{
        \texttt{SET TRANSACTION ISOLATION LEVEL READ COMMITTED;}
    } \\
    \cline{2-3}
    
    & \specialcell{Запуск транзакции 1 \\ \texttt{begin;}} & \specialcell{Запуск транзакции 2 \\ \texttt{begin;}}\\
    \hline

    $t_1$ & 
    \specialcell{
        Изменение имени судьи с \texttt{id\_judge = 201}\\
        \texttt{select id\_judge, name from judge}\\
        \texttt{where id\_judge = 201;}\\
        \texttt{update judge set name = 'Софа'}\\
        \texttt{where id\_judge = 201;}\\    
        \texttt{select id\_judge, name from judge}\\
        \texttt{where id\_judge = 201;}\\
    }
    \includegraphics[width=1\linewidth]{img/transaction1_1.png}
    & 
    \\
    \hline

    $t_2$ & 
    & 
    \specialcell{
        Получение имени судьи с \texttt{id\_judge = 201} \\
        и завершение транзакции\\ 
        \texttt{begin;} \\
        \texttt{select id\_judge, name from judge}\\
        \texttt{where id\_judge = 201;}\\
        \texttt{commit;}\\
    }
    \includegraphics[width=1\linewidth]{img/transaction1_2.png}
    \\
    \hline

    $t_3$ & 
    \specialcell{
        Отмена внесённых изменений транзакцией 1 \\
        \texttt{rollback;} \\
        % \texttt{select id\_judge, name from judge}\\
        % \texttt{where id\_judge = 201;}\\
    }
    \includegraphics[width=1\linewidth]{img/transaction1_3.png}
    & 
    \\
    \hline

    & \multicolumn{2}{p{0.9\textwidth}|}{
        В первой транзакции у судьи c \texttt{id\_judge = 201} изменяется имя с <<София>> на <<Софа>>. В момент после обновления вторая транзакция читает эту же запись из \texttt{judge}, однако в связи с отсутствием артефакта <<Грязное чтение>> она видит имя <<Софа>>, то есть значение до обновлений первой транзакции.
    }\\
    \hline
    \end{tabularx}
\end{table}


В таблице~\ref{tbl:unrepeatable_read} представлены транзакции, на примере которых демонстрируется наличие артефакта <<Неповторяемое чтение>>.

% \newpage
\begin{table}[h!]
    \centering
    % \addtocounter{table}{-1}
    \caption{Транзакции для демонстрации наличия артефакта <<Неповторяемое чтение>>.}
    \label{tbl:unrepeatable_read}
    \footnotesize
    \begin{tabularx}{\textwidth}{|c|X|X|}
    \hline
    t & Транзакция 1 & Транзакция 2 \\
    \hline

    \multirow{2}{*}{} 
    & \multicolumn{2}{c|}{
        \texttt{SET TRANSACTION ISOLATION LEVEL READ COMMITTED;}
    } \\
    \cline{2-3}
    
    & \specialcell{Запуск транзакции 1 \\ \texttt{begin;}} & \specialcell{Запуск транзакции 2 \\ \texttt{begin;}}\\
    \hline

    $t_1$ & 
    \specialcell{
        Получение имени судьи с \texttt{id\_judge = 201}\\ \\
        \texttt{select id\_judge, name from judge}\\
        \texttt{where id\_judge = 201;}\\
    }
    \includegraphics[width=1\linewidth]{img/transaction2_1.png}
    & 
    \\
    \hline

    $t_2$ & 
    & 
    \specialcell{
        Изменение имени судьи с \texttt{id\_judge = 201} \\
        и фиксация транзакции\\
        \texttt{select id\_judge, name from judge}\\
        \texttt{where id\_judge = 201;}\\
        \texttt{update judge set name = 'Софа'}\\
        \texttt{where id\_judge = 201;}\\    
        \texttt{select id\_judge, name from judge}\\
        \texttt{where id\_judge = 201;}\\
        \texttt{commit;}\\
    }
    \includegraphics[width=0.9\linewidth]{img/transaction2_2.png}
    \\
    \hline
    \end{tabularx}
\end{table}


\newpage
\begin{table}[h!]
    \centering
    \addtocounter{table}{-1}
    \caption{Транзакции для демонстрации наличия артефакта <<Неповторяемое чтение>> (продолжение).}
    \label{tbl:unrepeatable_read}
    \footnotesize
    \begin{tabularx}{\textwidth}{|c|X|X|}
    \hline
    t & Транзакция 1 & Транзакция 2 \\
    \hline

    $t_3$ & 
    \specialcell{
        Получение имени судьи с \texttt{id\_judge = 201}\\
        и фиксация транзакции \\ \\
        \texttt{select id\_judge, name from judge}\\
        \texttt{where id\_judge = 201;}\\
        \texttt{commit;}\\
    }
    \includegraphics[width=1\linewidth]{img/transaction2_3.png}
    & 
    \\
    \hline

    & \multicolumn{2}{p{0.9\textwidth}|}{
        В первой транзакции происходит получение имени судьи с  \texttt{id\_judge = 201} -- выводится имя <<София>>. После чего эта же запись обновляется во второй транзакции, имя изменяется на <<Софа>>, изменения фиксируются и вторая транзакция  успешно завершается. Затем первая транзакция повторно считывает эту запись и получает обновлённые данные -- имя <<Софа>>, из-за наличия артефакта <<Неповторяемое чтение>>. 
    }\\
    \hline
    \end{tabularx}
\end{table}


В таблице~\ref{tbl:fantom} представлены транзакции, на примере которых демонстрируется наличие артефакта <<Фантомы>>.

% \newpage
\begin{table}[h!]
    \centering
    % \addtocounter{table}{-1}
    \caption{Транзакции для демонстрации наличия артефакта <<Фантомы>>.}
    \label{tbl:fantom}
    \footnotesize
    \begin{tabularx}{\textwidth}{|c|X|X|}
    \hline
    t & Транзакция 1 & Транзакция 2 \\
    \hline

    \multirow{2}{*}{} 
    & \multicolumn{2}{c|}{
        \texttt{SET TRANSACTION ISOLATION LEVEL READ COMMITTED;}
    } \\
    \cline{2-3}
    
    & \specialcell{Запуск транзакции 1 \\ \texttt{begin;}} & \specialcell{Запуск транзакции 2 \\ \texttt{begin;}}\\
    \hline

    $t_1$ & 
    \specialcell{
        Получение имён судей с \texttt{id\_judge >= 250}\\ \\
        \texttt{select id\_judge, name from judge}\\
        \texttt{where id\_judge >= 250;}\\
    }
    \includegraphics[width=1\linewidth]{img/transaction3_1.png}
    & 
    \\
    \hline

    \end{tabularx}
\end{table}

\newpage
\begin{table}[h!]
    \centering
    \addtocounter{table}{-1}
    \caption{Транзакции для демонстрации наличия артефакта <<Фантомы>> (продолжение).}
    \label{tbl:fantom}
    \footnotesize
    \begin{tabularx}{\textwidth}{|c|X|X|}
    \hline
    t & Транзакция 1 & Транзакция 2 \\
    \hline


    $t_2$ & 
    & 
    \specialcell{
        Добавление нового судьи и фиксация \\ транзакции\\ \\
        \texttt{select id\_judge, name from judge}\\
        \texttt{where id\_judge >= 250;}\\
        \texttt{insert into judge (name, surname,}\\ 
        \texttt{patronymic, category)}\\
        \texttt{values ('вика', 'иванова', 'петровна',}\\
        \texttt{'высшая');}\\
        \texttt{select id\_judge, name from judge}\\
        \texttt{where id\_judge >= 250;}\\
        \texttt{commit;}\\
    }
    \includegraphics[width=1\linewidth]{img/transaction3_2.png}
    \\
    \hline

    $t_3$ & 
    \specialcell{
        Получение имён судей с \texttt{id\_judge >= 250}\\ \\
        \texttt{select id\_judge, name from judge}\\
        \texttt{where id\_judge >= 250;}\\
        \texttt{commit;}\\
    }
    \includegraphics[width=1\linewidth]{img/transaction3_3.png}
    & 
    \\
    \hline

    & \multicolumn{2}{p{0.9\textwidth}|}{
        В первой транзакции происходит получение имён судей из таблицы \texttt{judge} с \texttt{id\_judge >= 250}, в ответ выводится один судья с именем <<анна>>. После чего в эту таблицу добовляется новый судья во второй транзакции с именем <<вика>>, изменения фиксируются и вторая транзакция успешно завершается. Затем первая транзакция повторно получает записи с \texttt{id\_judge >= 250}. В этот раз, по мимо судьи с именем <<анна>>, она также получает данные о записи, добавленной второй транзакцией -- судье с именем <<вика>>, эта запись и является артефактом <<Фантом>>. 
    }\\
    \hline
    \end{tabularx}
\end{table}

\newpage
\section*{Заключение}
\addcontentsline{toc}{section}{Заключение}
В ходе освоения данного курса было выполнено пять лабораторных работ:
\begin{enumerate}
	\item Создано представление, инкапсулирующее запрос. Продемонстрирована невозможность модификации представления; написан запрос, использующий в себе созданное представление.
	\item Создана таблица подсчёта количества соревнований для каждого судьи. Также созданы триггеры, автоматизирующие сбор статистики в таблице.
	\item Созданы два пользователя с различными правами доступа. Первый пользователь наделён правами только на просмотр представления, а второй наделён правами просмотра, вставки, удаления и обновления данных во всех таблицах, участвующих в представлении. Продемонстрировано поведение СУБД при различных операциях для каждого пользователя, в том числе при недопустимых.
	\item Созданы процедура и функция. Функция принимает на вход фамилию, имя и отчество человека и возвращает фамилию и его инициалы.
	\item Управление транзакциями. Задан уровень изоляции транзакций как Read Committed и продемонстрировано отсутствие артефакта <<Грязное чтение>> и наличие артефактов <<Неповторяемое чтение>> и <<Фантомы>>.
\end{enumerate}

На работу было потрачено около 2-х месяцев, за которые было написано более пятисот строк кода.

Работа была выполнена в системе управления базами данных PostgreSQL 16.2.

Полученные знания могут быть и будут использованы в работе над последующими проектами и заданиями.


\newpage
\section*{Список литературы}
\addcontentsline{toc}{section}{Список литературы}

\vspace{-1.5cm}
\begin{thebibliography}{0}
    \bibitem{vostrov}
    MySQL Documentation URL: \url{https://dev.mysql.com/doc/}, Дата обращения: 01.11.2024
    \bibitem{vostrov}
    PostgreSQL documentation URL: \url{https://www.postgresql.org/docs/}, Дата обращения: 01.11.2024
\end{thebibliography}


\end{document}