2038 lines
111 KiB
TeX
2038 lines
111 KiB
TeX
\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} %для перечислений
|
||
|
||
\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}{Введение}
|
||
|
||
В этом отчёте рассматривается разработка базы данных для информационной системы, предназначенной для организации соревнований по стрельбе из лука. Правила и порядок проведения соревнований в России регламентируются Всемирной федерацией стрельбы из лука (FITA) и Российской федерацией по стрельбе из лука (РФСЛ). Информационная система могла бы упростить организацию соревнований и сделать их более доступными для большего числа спортивных клубов, спортсменов и судей.
|
||
|
||
\newpage
|
||
|
||
\section {Аналитика предметной области}
|
||
\subsection{Вид спорта}
|
||
Стрельба из лука, олимпийский вид спорта с 1900 года. Правила и порядок проведения соревнований в России регламентируются Всемирной федерацией стрельбы из лука (FITA) и Российской федерацией по стрельбе из лука (РФСЛ). РФСЛ проводит несколько десятков соревнований по различным видам стрельбы из лука ежегодно. Спорт также приобретает популярность в любительской среде, например, только в Санкт-Петербурге функционируют около двух десятков спортивных стрелковых клубов и лучных клубов, где может тренировать любой желающий. Частные клубы также регулярно проводят спортивные соревнования.
|
||
|
||
В стрельбе из лука используются различные виды луков: классический или Олимпийский лук, с натяжением от 15 до 20 кг и скоростью стрелы около 240 км/ч, и блочный лук, с натяжением от 25 до 30 кг, что позволяет достигать скорости стрелы до 360 км/ч. Также применяются составные, периферийные, длинные и ассиметричные луки.
|
||
|
||
Также спортсмены соревнуются в различных видах стрельбы и на различных дистанциях, например, в помещении обычно стреляют с дистанций 18 м, 30 м и 50 м, на открытом воздухе на дистанциях до 90 м. Кроме традиционной стрельбы по мишеням, существуют специальные дисциплины, такие как 3Д стрельба из лука, арчери-кросс, арчери-биатлон и другие.
|
||
|
||
Именно индивидуальная стрельба по мишеням входит в олимпийские игры и является самым популярным видом стрельбы, поэтому в этой работе в первую очередь рассматриваются именно такие соревнования.
|
||
|
||
|
||
\subsection{Особенности соревнований по стрельбе из лука}
|
||
Когда организатор проводит соревнования, в первую очередь он должен определиться с местом и датами проведения. После этого он должен предоставить спортсменам возможность оставить заявку на соревнования, сейчас многие используют для этого Yandex или Google формы, в этой заявке спортсмены указывают персональные данные, а также спортивный разряд, тип лука, федерацию или клуб, от имени которого они участвуют в соревновании. По мимо спортсменов важно подобрать судей. Соответственно судьи также должны оставить заявку, в которой они указывают свои персональные данные, а также свою судейскую категорию, номер и дату приказа об её присвоении. Заявки от спортсменов и судей обычно начинают приниматься за две-три недели до начала соревнования.
|
||
|
||
\vspace{10pt}
|
||
Соревнования по стрельбе из лука можно разделить на следующие этапы:
|
||
\vspace{-5pt}
|
||
\begin{enumerate}
|
||
\item Регистрация прибывших спортсменов.
|
||
\item Регистрация прибывших судей.
|
||
\item Распределение спортсменов по дивизионам, например: "Мужчины – блочный лук – 50м", "Женщины – классический лук – 18м" и так далее.
|
||
\item Жеребьёвка, во время которой спортсмены случайным образом распределяются по щитам и мишеням. Обычно щиты нумеруются цифрами, а мишени внутри щита буквами. Пример щитов с мишенями изображён на Рис.~\ref{fig:shield}.
|
||
|
||
\begin{figure}[h]
|
||
\centering
|
||
\includegraphics[width=0.6\linewidth]{img/shield.jpg}
|
||
\caption{Пример щита с мишенями}
|
||
\label{fig:shield}
|
||
\end{figure}
|
||
|
||
\item Выдача каждому участнику карточки, в которой указывается, по какой мишени он будет стрелять. В эту же карточку судьи записывают результаты участника. Пример карточки участника представлен на Рис.~\ref{fig:card}.
|
||
|
||
\begin{figure}[h]
|
||
\centering
|
||
\includegraphics[width=1\linewidth]{img/card.jpg}
|
||
\caption{Пример карточки участника}
|
||
\label{fig:card}
|
||
\end{figure}
|
||
|
||
\item Определение списка судей и мишеней, у которых они будут записывать результаты. В квалификационном этапе могут участвовать судьи с любой судейской категорией.
|
||
\item Проведение двух этапов квалификации, где каждый спортсмен выполняет по 30 или 60 выстрелов. Результаты квалификации определяют содержание протоколов, которые затем печатаются и вывешиваются для всеобщего обозрения.
|
||
\item По результатам квалификации определяются лучшие спортсмены в каждом дивизионе, которые попадают в финал. Финалы проводятся по стандартной олимпийской системе.
|
||
\item Определение списка судей, фиксирующих результаты финальных стрельб. Обычно в финалах участвуют судьи, начиная с определённой судейской категории.
|
||
\item Подведение итогов соревнований и формирование итогового протокола, содержащего информацию о выступлениях спортсменов, имена победителей и так далее.
|
||
\end{enumerate}
|
||
|
||
|
||
% \newpage
|
||
% \phantom{.}
|
||
\newpage
|
||
|
||
\subsection {Цели информационной системы}
|
||
В информационной системе для организации соревнований по стрельбе из лука могут быть реализованы следующие функции:
|
||
\vspace{-5pt}
|
||
\begin{enumerate}
|
||
\item Объективная фиксация результата. Судья может фотографировать мишени после каждой серии выстрелов и загружать их в систему. Таким образом будет гораздо проще разрешить любую спорную ситуацию.
|
||
\item История выступлений спортсмена на всех соревнованиях может храниться в системе. Таким образом спортсмен сможет следить за тенденцией как своих результатов, так и результатов своих соперников.
|
||
\item Загруженные в систему результаты могут в реальном времени выводиться на интерактивное табло. Это не только упростит жизнь организаторам, так как им не нужно будет каждый раз печатать и вывешивать промежуточные результат, но и позволит спортсменам видеть результаты всех своих соперников в реальном времени.
|
||
\item Сократится время ожидания при формировании промежуточных результатов соревнований, потому что система позволит быстро сортировать участников по результатам, группировать по дивизионам, то есть по полу и типу лука, отбирать лучших спортсменов для участия в финалах.
|
||
\item Автоматическое вычисление разрядов. Система, используя сохранённые результаты, может автоматически выставлять выполненные спортивные разряды.
|
||
\item Организаторы смогут выбрать судей в системе и пригласить на свои соревнования. Организатор может, например, отфильтровать судей по региону, спортивной федерации, судейской категории.
|
||
\item Система может в автоматическом режиме переносить результаты спортсменов в промежуточные и итоговые протоколы, которые необходимо составлять организатору соревнований.
|
||
\end{enumerate}
|
||
|
||
\subsection{Роли}
|
||
В системе можно выделить несколько ролей:
|
||
\begin{enumerate}
|
||
\item \textbf{Организаторы}
|
||
\begin{itemize}
|
||
\item определяют место и даты проведения соревнований;
|
||
\item обеспечивают возможность регистрации для спортсменов и судей, часто с использованием онлайн-форм (Yandex или Google формы);
|
||
\item организуют жеребьёвку и распределение спортсменов по дивизионам;
|
||
\item обрабатывают результаты квалификаций и финалов, формируют и публикуют итоговые протоколы;
|
||
\item обеспечивают выполнение всех регламентирующих требований от Всемирной федерации стрельбы из лука (FITA) и Российской федерации по стрельбе из лука (РФСЛ).
|
||
\end{itemize}
|
||
|
||
\item \textbf{Спортсмены}
|
||
\begin{itemize}
|
||
\item участвуют в соревнованиях, зарегистрировавшись и предоставив необходимые данные (персональные данные, спортивный разряд, тип лука, федерация или клуб);
|
||
\item участвуют в квалификационных этапах и финалах, производя установленные количества выстрелов.
|
||
\end{itemize}
|
||
|
||
\item \textbf{Судьи}
|
||
\begin{itemize}
|
||
\item осуществляют судейство на соревнованиях, зарегистрировавшись и указав свою судейскую категорию, номер и дату приказа о её присвоении;
|
||
\item отвечают за фиксирование результатов серий выстрелов спортсменов.
|
||
\end{itemize}
|
||
|
||
\end{enumerate}
|
||
|
||
\subsection{Сущности}
|
||
В этом разделе представлены основные сущности системы и их атрибуты.
|
||
|
||
\begin{enumerate}
|
||
\item \textbf{Организатор} -- организует соревнования, одобряет заявки спортсменов и выбирает судей.
|
||
\begin{itemize}
|
||
\item Название организации или ФИО;
|
||
\item Логотип.
|
||
\end{itemize}
|
||
|
||
\item \textbf{Соревнование} -- ключевая сущность, мероприятие, в котором участвуют спортсмены и судьи. Заявки участников и судей, протоколы и результаты -- все эти сущности распределены по соревнованиям.
|
||
\begin{itemize}
|
||
\item Название;
|
||
\item Логотип;
|
||
\item Адрес проведения;
|
||
\item Дата начала;
|
||
\item Дата окончания.
|
||
\end{itemize}
|
||
|
||
\item \textbf{Спортсмен} -- участвует в соревнованиях, оставляя завки. Производит серии выстрелов в квалификационных этапах и финалах.
|
||
\begin{itemize}
|
||
\item ФИО;
|
||
\item Пол;
|
||
\item Спортивный разряд;
|
||
\item Тип лука;
|
||
\item Спортивная федерация;
|
||
\item Спортивный клуб;
|
||
\item Регион.
|
||
\end{itemize}
|
||
|
||
\item \textbf{Заявка участника} -- заявка оставляемая спортсменом, для участия в конкретном дивизионе конкретного соревнования. Одобряется организатором.
|
||
\begin{itemize}
|
||
\item Информация о спортсмене;
|
||
\item Отметка о регистрации.
|
||
\end{itemize}
|
||
|
||
\item \textbf{Судья} -- участвует в соревновании, оставляя заявку. Фиксирует результаты участников.
|
||
\begin{itemize}
|
||
\item ФИО;
|
||
\item Судейская категория;
|
||
\item Регион;
|
||
\item Спортивная федерация.
|
||
\end{itemize}
|
||
|
||
\item \textbf{Заявка судьи} -- заявка, оставляемая судьёй, для участия в конкретном соревновании. Одобряется организатором.
|
||
\begin{itemize}
|
||
\item Информация о судье;
|
||
\item Отметка о регистрации.
|
||
\end{itemize}
|
||
|
||
\item \textbf{Протокол (итоговый)} -- документ, содрежащий итоги соревнования, результаты всех этапов.
|
||
\begin{itemize}
|
||
\item Название;
|
||
\item Дата формирования;
|
||
\item Файл.
|
||
\end{itemize}
|
||
|
||
\item \textbf{Позиция для стрельбы} -- три мишени друг под другом, по которым участник ведёт стрельбу. Затем по этим же мишеням судья определяет количество очков, заработанных участником.
|
||
\begin{itemize}
|
||
\item Индекс мишени;
|
||
\item Номер щита.
|
||
\end{itemize}
|
||
|
||
\item \textbf{Серия выстрелов} -- серия из трёх выстрелов, которые производит спортсмен на своей позиции для стрельбы. Судья фиксирует результаты серии.
|
||
\begin{itemize}
|
||
\item Ссылка на фотографию.
|
||
\end{itemize}
|
||
|
||
\item \textbf{Очки} -- очки получаемые за серию выстрелов.
|
||
\begin{itemize}
|
||
\item Количество.
|
||
\end{itemize}
|
||
|
||
\item \textbf{Этап соревнования} -- бывают квалификационные и финальные этапы. Количество серий выстрелов может варьироваться в зависимости от этапа.
|
||
\begin{itemize}
|
||
\item Тип этапа;
|
||
\item Количество серий.
|
||
\end{itemize}
|
||
|
||
\item \textbf{Результат} -- сумма очков определённого спортсмена в определённом этапе и дивизионе соревнования.
|
||
\begin{itemize}
|
||
\item Сумма очков за этап.
|
||
\end{itemize}
|
||
|
||
\item \textbf{Дивизион} -- группа участников соревнования с одним полом, типом лука и дистанцией для стрельбы.
|
||
\begin{itemize}
|
||
\item МСМК;
|
||
\item МС;
|
||
\item КМС;
|
||
\item 1 разряд;
|
||
\item 2 разряд;
|
||
\item 3 разряд;
|
||
\item Название;
|
||
\item Тип лука;
|
||
\item Пол;
|
||
\item Дистанция.
|
||
\end{itemize}
|
||
|
||
\end{enumerate}
|
||
|
||
\subsection{Диаграммы}
|
||
На основании вышеизложенных разделов были составлены ER-диаграмма (Рис.~3) и диаграмма объектов (Рис.~4).
|
||
|
||
ER-диаграмму можно прочитать следующим образом:
|
||
\begin{enumerate}
|
||
\item Организатор проводит соревнование;
|
||
\item Организатор распределяет спортсменов по позициям для стрельбы;
|
||
\item Организатор утверждает заявки участников;
|
||
\item Организатор выбирает дивизионы для соревнования;
|
||
\item Спортсмен участвует в соревновании;
|
||
\item Спортсмен подаёт заявку участника;
|
||
\item Спортсмен производит серию выстрелов на позиции для стрельбы в определённом дивизионе и этапе соревнования;
|
||
\item Судья судит соревнование;
|
||
\item Судья подаёт заявку судьи на соревнование;
|
||
\item Судья назначает очки за серию выстрелов;
|
||
\item Судья суммирует очки за этап соревнования в результат;
|
||
\item Судья заносит результат в протокол.
|
||
\end{enumerate}
|
||
|
||
% ER-диаграмма
|
||
\includepdf[pages={1}, fitpaper, pagecommand={%
|
||
\thispagestyle{empty}%
|
||
\begin{tikzpicture}[remember picture, overlay]
|
||
\node at (current page.south) [anchor=north, yshift=35pt] {\large{Рис 3. ER-диаграмма}};
|
||
\end{tikzpicture}%
|
||
}]{pdf/er_diagram.pdf}
|
||
|
||
% Объектная диаграмма
|
||
\includepdf[pages={1}, fitpaper, pagecommand={%
|
||
\thispagestyle{empty}%
|
||
\begin{tikzpicture}[remember picture, overlay]
|
||
\node at (current page.south) [anchor=north, yshift=35pt] {\large{Рис 4. Объектная диаграмма}};
|
||
\end{tikzpicture}%
|
||
}]{pdf/obj_diagram.pdf}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
\newpage
|
||
\section{Проектирование базы данных}
|
||
|
||
\subsection{Технология работы с СУБД}
|
||
В качестве системы управления базами данных была выбрана PostgreSQL. Она является одной из самых популярных бесплатных СУБД и поддерживает весь необходимый функционал.
|
||
|
||
PostgreSQL предоставляет пользователю как графический, так и консольный интерфейсы для работы с базами данных и выполнения SQL запросов. Для того чтобы выполнять команды в консоли PostgreSQL, необходимо сначала подключиться к нужной базе данных. Это можно сделать с помощью команды:
|
||
|
||
\begin{verbatim}
|
||
psql -U имя_пользователя -d имя_базы_данных
|
||
\end{verbatim}
|
||
|
||
Для создания таблицы используется команда \texttt{CREATE TABLE}. Эта команда позволяет определить структуру таблицы, включая имена столбцов и их типы данных. Пример создания таблицы \texttt{sportsman}, которая содержит информацию о спортсменах:
|
||
|
||
\begin{verbatim}
|
||
CREATE TABLE sportsman (
|
||
id_sportsman SERIAL PRIMARY KEY,
|
||
name VARCHAR(100) NOT NULL,
|
||
gender VARCHAR(10) NOT NULL,
|
||
birthday DATE,
|
||
region VARCHAR(100),
|
||
club VARCHAR(100),
|
||
federation VARCHAR(100),
|
||
category VARCHAR(100)
|
||
);
|
||
\end{verbatim}
|
||
|
||
Для удаления таблицы и всех данных, которые в неё содержатся, используется команда \texttt{DROP TABLE}. Пример удаления таблицы \texttt{sportsman}:
|
||
|
||
\begin{verbatim}
|
||
DROP TABLE sportsman;
|
||
\end{verbatim}
|
||
|
||
Чтобы просмотреть структуру существующей таблицы, можно использовать команду \texttt{\textbackslash d}, которая отображает схему таблицы, включая информацию о столбцах и их типах данных. Пример использования команды для таблицы \texttt{sportsman}:
|
||
|
||
\begin{verbatim}
|
||
\d sportsman
|
||
\end{verbatim}
|
||
|
||
Результат выполнения команды представлен на Рис.~\ref{fig:slash_d}.
|
||
|
||
\begin{figure}[h]
|
||
\centering
|
||
\includegraphics[width=1\linewidth]{img/slash_d.png}
|
||
\caption{Пример работы команды \textbackslash d}
|
||
\label{fig:slash_d}
|
||
\end{figure}
|
||
|
||
Для вставки данных в таблицу используется команда \texttt{INSERT INTO}. Пример добавления новой записи в таблицу \texttt{sportsman}:
|
||
|
||
\begin{verbatim}
|
||
INSERT INTO
|
||
sportsman (
|
||
name,
|
||
gender,
|
||
birthday,
|
||
region,
|
||
club,
|
||
federation,
|
||
category
|
||
)
|
||
VALUES
|
||
(
|
||
'Иван Иванов',
|
||
'Мужчина',
|
||
'01.01.1990',
|
||
'Санкт-Петербург',
|
||
'ЛК Парадокс лучника',
|
||
'РФСЛ',
|
||
'КМС'
|
||
);
|
||
\end{verbatim}
|
||
|
||
Для удаления данных из таблицы используется команда \texttt{DELETE}. Пример удаления записи о спортсмене с \texttt{id\_sportsman = 1} из таблицы \texttt{sportsman}:
|
||
|
||
\begin{verbatim}
|
||
DELETE FROM sportsman
|
||
WHERE id_sportsman = 1;
|
||
\end{verbatim}
|
||
|
||
|
||
\subsection{Таблицы}
|
||
В этом разделе представлены таблицы метаданных, они дополняют схемы базы данных, приведённые на Рис.~6 и Рис.~7. В этих таблицах 1-12 все атрибуты таблиц базы данных и их типы данных.
|
||
|
||
Для большей части текстовых полей установлен тип \texttt{VARCHAR(100)}. Ста символов хватает для хранения большинства имён и названий. Самое длинное имя в имеющихся данных содержит 21 символ, самое длинное название клуба -- 47 символов, регион с самым длинным названием в России это <<Ханты-Мансийский автономный округ — Югра>> -- 40 символов. Для ссылок на логотипы и файлы используется \texttt{VARCHAR(1024)}.
|
||
|
||
Для хранения чисел, не считая идентификаторов, используется тип \texttt{SMALLINT}. В нём можно хранить значения до 32,767, этого более чем достаточно для данной задачи. Наибольшие числовые значения, которые требуется хранить в базе данных, это суммы очков -- числа от нуля до нескольких сотен. Для идентификаторов используется специфический для PostgreSQL тип \texttt{SERIAL}, который, по сути, является псевдонимом для типа \texttt{INTEGER} с автоинкрементом.
|
||
|
||
В нескольких местах используется тип \texttt{DATE} для хранения дат.
|
||
|
||
\begin{table}[h!]
|
||
\centering
|
||
\caption{Организатор}
|
||
\footnotesize
|
||
\begin{tabularx}{\textwidth}{|c|c|X|X|X|X|}
|
||
\hline
|
||
\textbf{KEY} & \textbf{Ссылка} & \textbf{Название EN} & \textbf{Название RU} & \textbf{Тип атрибута} & \textbf{NULL/NOT NULL} \\
|
||
\hline
|
||
PK & - & id\_organizer & id\_Организатор & INTEGER & NOT NULL \\
|
||
- & - & name & Название & VARCHAR(100) & NOT NULL \\
|
||
- & - & logo & Логотип & VARCHAR(1024) & NULL \\
|
||
\hline
|
||
\end{tabularx}
|
||
\end{table}
|
||
|
||
\begin{table}[h!]
|
||
\centering
|
||
\caption{Судья}
|
||
\footnotesize
|
||
\begin{tabularx}{\textwidth}{|c|c|X|X|X|X|}
|
||
\hline
|
||
\textbf{KEY} & \textbf{Ссылка} & \textbf{Название EN} & \textbf{Название RU} & \textbf{Тип атрибута} & \textbf{NULL/NOT NULL} \\
|
||
\hline
|
||
PK & - & id\_judge & id\_Судья & INTEGER & NOT NULL \\
|
||
- & - & name & Имя & VARCHAR(100) & NOT NULL \\
|
||
- & - & category & Категория & VARCHAR(100) & NOT NULL \\
|
||
- & - & region & Регион & VARCHAR(100) & NULL \\
|
||
- & - & federation & Федерация & VARCHAR(100) & NULL \\
|
||
\hline
|
||
\end{tabularx}
|
||
\end{table}
|
||
|
||
\begin{table}[h!]
|
||
\centering
|
||
\caption{Спортсмен}
|
||
\footnotesize
|
||
\begin{tabularx}{\textwidth}{|c|c|X|X|X|X|}
|
||
\hline
|
||
\textbf{KEY} & \textbf{Ссылка} & \textbf{Название EN} & \textbf{Название RU} & \textbf{Тип атрибута} & \textbf{NULL/NOT NULL} \\
|
||
\hline
|
||
PK & - & id\_sportsman & id\_Спортсмен & INTEGER & NOT NULL \\
|
||
- & - & name & Имя & VARCHAR(100) & NOT NULL \\
|
||
- & - & gender & Пол & VARCHAR(10) & NOT NULL \\
|
||
- & - & birthday & Дата\_рождения & DATE & NULL \\
|
||
- & - & region & Регион & VARCHAR(100) & NULL \\
|
||
- & - & club & Клуб & VARCHAR(100) & NULL \\
|
||
- & - & federation & Федерация & VARCHAR(100) & NULL \\
|
||
- & - & category & Категория & VARCHAR(100) & NULL \\
|
||
\hline
|
||
\end{tabularx}
|
||
\end{table}
|
||
|
||
\begin{table}[h!]
|
||
\centering
|
||
\caption{Дивизион}
|
||
\footnotesize
|
||
\begin{tabularx}{\textwidth}{|c|c|X|X|X|X|}
|
||
\hline
|
||
\textbf{KEY} & \textbf{Ссылка} & \textbf{Название EN} & \textbf{Название RU} & \textbf{Тип атрибута} & \textbf{NULL/NOT NULL} \\
|
||
\hline
|
||
PK & - & id\_division & id\_Дивизион & INTEGER & NOT NULL \\
|
||
- & - & title & Название & VARCHAR(100) & NOT NULL \\
|
||
- & - & gender & Пол & VARCHAR(10) & NOT NULL \\
|
||
- & - & bow\_type & Тип\_лука & VARCHAR(100) & NOT NULL \\
|
||
- & - & distance & Дистанция & SMALLINT & NOT NULL \\
|
||
- & - & msmk & МСМК & SMALLINT & NOT NULL \\
|
||
- & - & ms & МС & SMALLINT & NOT NULL \\
|
||
- & - & kms & КМС & SMALLINT & NOT NULL \\
|
||
- & - & category\_1 & Категория\_1 & SMALLINT & NOT NULL \\
|
||
- & - & category\_2 & Категория\_2 & SMALLINT & NOT NULL \\
|
||
- & - & category\_3 & Категория\_3 & SMALLINT & NOT NULL \\
|
||
\hline
|
||
\end{tabularx}
|
||
\end{table}
|
||
|
||
\begin{table}[h!]
|
||
\centering
|
||
\caption{Соревнование}
|
||
\footnotesize
|
||
\begin{tabularx}{\textwidth}{|c|c|X|X|X|X|}
|
||
\hline
|
||
\textbf{KEY} & \textbf{Ссылка} & \textbf{Название EN} & \textbf{Название RU} & \textbf{Тип атрибута} & \textbf{NULL/NOT NULL} \\
|
||
\hline
|
||
PK & - & id\_competition & id\_Соревнование & INTEGER & NOT NULL \\
|
||
- & - & title & Название & VARCHAR(100) & NOT NULL \\
|
||
- & - & address & Адрес & VARCHAR(100) & NOT NULL \\
|
||
- & - & logo & Логотип & VARCHAR(1024) & NULL \\
|
||
- & - & start\_date & Дата\_начала & DATE & NOT NULL \\
|
||
- & - & end\_date & Дата\_окончания & DATE & NOT NULL \\
|
||
FK & \specialcell{Организа-\\тор.id\_Ор-\\ганизатор} & id\_organizer & id\_Организатор & INTEGER & NOT NULL \\
|
||
\hline
|
||
\end{tabularx}
|
||
\end{table}
|
||
|
||
\begin{table}[h!]
|
||
\centering
|
||
\caption{Дивизион в соревновании}
|
||
\footnotesize
|
||
\begin{tabularx}{\textwidth}{|c|c|X|X|X|X|}
|
||
\hline
|
||
\textbf{KEY} & \textbf{Ссылка} & \textbf{Название EN} & \textbf{Название RU} & \textbf{Тип атрибута} & \textbf{NULL/NOT NULL} \\
|
||
\hline
|
||
PK & - & \specialcell{id\_division\_\\in\_competition} & \specialcell{id\_Дивизион\_\\в\_соревновании} & INTEGER & NOT NULL \\
|
||
FK & \specialcell{Дивизион.\\id\_Дивизион} & id\_division & id\_Дивизион & INTEGER & NOT NULL \\
|
||
FK & \specialcell{Соревнова-\\ние.id\_Со-\\ревнование} & id\_competition & id\_Соревнование & INTEGER & NOT NULL \\
|
||
\hline
|
||
\end{tabularx}
|
||
\end{table}
|
||
|
||
\begin{table}[h!]
|
||
\centering
|
||
\caption{Протокол}
|
||
\footnotesize
|
||
\begin{tabularx}{\textwidth}{|c|c|X|X|X|X|}
|
||
\hline
|
||
\textbf{KEY} & \textbf{Ссылка} & \textbf{Название EN} & \textbf{Название RU} & \textbf{Тип атрибута} & \textbf{NULL/NOT NULL} \\
|
||
\hline
|
||
PK & - & id\_protocol & id\_Протокол & INTEGER & NOT NULL \\
|
||
- & - & name & Название & VARCHAR(100) & NOT NULL \\
|
||
- & - & date & Дата & DATE & NOT NULL \\
|
||
- & - & file & Файл & VARCHAR(1024) & NOT NULL \\
|
||
FK & \specialcell{Соревнова-\\ние.id\_Со-\\ревнование} & id\_competition & id\_Соревнование & INTEGER & NOT NULL \\
|
||
\hline
|
||
\end{tabularx}
|
||
\end{table}
|
||
|
||
\begin{table}[h!]
|
||
\centering
|
||
\caption{Заявка участника}
|
||
\footnotesize
|
||
\begin{tabularx}{\textwidth}{|c|c|X|X|X|X|}
|
||
\hline
|
||
\textbf{KEY} & \textbf{Ссылка} & \textbf{Название EN} & \textbf{Название RU} & \textbf{Тип атрибута} & \textbf{NULL/NOT NULL} \\
|
||
\hline
|
||
PK & - & \specialcell{id\_participant\\\_request} & \specialcell{id\_Заявка\\\_участника} & INTEGER & NOT NULL \\
|
||
- & - & name & Имя & VARCHAR(100) & NOT NULL \\
|
||
- & - & gender & Пол & VARCHAR(10) & NOT NULL \\
|
||
- & - & birthday & Дата\_рождения & DATE & NOT NULL \\
|
||
- & - & region & Регион & VARCHAR(100) & NULL \\
|
||
- & - & club & Клуб & VARCHAR(100) & NULL \\
|
||
- & - & federation & Федерация & VARCHAR(100) & NULL \\
|
||
- & - & category & Категория & VARCHAR(100) & NOT NULL \\
|
||
- & - & is\_registered & Зарегистрирован & BOOLEAN & NOT NULL \\
|
||
FK & \specialcell{Соревнова-\\ние.id\_Со-\\ревнование} & id\_competition & id\_Соревнование & INTEGER & NOT NULL \\
|
||
FK & \specialcell{Спортсмен\\.id\_Спорт-\\смен} & id\_sportsman & id\_Спортсмен & INTEGER & NOT NULL \\
|
||
FK & \specialcell{Дивизион\\.id\_Диви-\\зион} & id\_division & id\_Дивизион & INTEGER & NOT NULL \\
|
||
\hline
|
||
\end{tabularx}
|
||
\end{table}
|
||
|
||
\begin{table}[h!]
|
||
\centering
|
||
\caption{Этап соревнования}
|
||
\footnotesize
|
||
\begin{tabularx}{\textwidth}{|c|c|X|X|X|X|}
|
||
\hline
|
||
\textbf{KEY} & \textbf{Ссылка} & \textbf{Название EN} & \textbf{Название RU} & \textbf{Тип атрибута} & \textbf{NULL/NOT NULL} \\
|
||
\hline
|
||
PK & - & \specialcell{id\_competition\\\_stage} & \specialcell{id\_Этап\\\_соревнования} & INTEGER & NOT NULL \\
|
||
- & - & title & Название & VARCHAR(100) & NOT NULL \\
|
||
- & - & count\_of\_series & \specialcell{Количество\\\_серий} & SMALLINT & NOT NULL \\
|
||
\hline
|
||
\end{tabularx}
|
||
\end{table}
|
||
|
||
\begin{table}[h!]
|
||
\centering
|
||
\caption{Результат в этапе}
|
||
\footnotesize
|
||
\begin{tabularx}{\textwidth}{|c|c|X|X|X|X|}
|
||
\hline
|
||
\textbf{KEY} & \textbf{Ссылка} & \textbf{Название EN} & \textbf{Название RU} & \textbf{Тип атрибута} & \textbf{NULL/NOT NULL} \\
|
||
\hline
|
||
PK & - & \specialcell{id\_result\\\_in\_stage} & \specialcell{id\_Результат\\\_в\_этапе} & INTEGER & NOT NULL \\
|
||
- & - & score & Очки & SMALLINT & NOT NULL \\
|
||
- & - & shield\_index & Индекс\_щита & SMALLINT & NOT NULL \\
|
||
- & - & target\_index & Индекс\_мишени & CHAR(1) & NOT NULL \\
|
||
FK & \specialcell{Заявка\_\\участника.\\id\_Заявка\_\\участника} & \specialcell{id\_participant\\\_request} & \specialcell{id\_Заявка\\\_участника} & INTEGER & NOT NULL \\
|
||
FK & \specialcell{Дивизион\\.id\_Диви-\\зион} & id\_division & id\_Дивизион & INTEGER & NOT NULL \\
|
||
FK & \specialcell{Этап\_соре-\\внования.\\id\_Этап\_со-\\ревнования} & \specialcell{id\_competition\\\_stage} & \specialcell{id\_Этап\\\_соревнования} & INTEGER & NOT NULL \\
|
||
\hline
|
||
\end{tabularx}
|
||
\end{table}
|
||
|
||
\begin{table}[h!]
|
||
\centering
|
||
\caption{Заявка судьи}
|
||
\footnotesize
|
||
\begin{tabularx}{\textwidth}{|c|c|X|X|X|X|}
|
||
\hline
|
||
\textbf{KEY} & \textbf{Ссылка} & \textbf{Название EN} & \textbf{Название RU} & \textbf{Тип атрибута} & \textbf{NULL/NOT NULL} \\
|
||
\hline
|
||
PK & - & \specialcell{id\_judge\\\_request} & \specialcell{id\_Заявка\\\_судьи} & INTEGER & NOT NULL \\
|
||
- & - & name & Имя & VARCHAR(100) & NOT NULL \\
|
||
- & - & category & Категория & VARCHAR(100) & NOT NULL \\
|
||
- & - & region & Регион & VARCHAR(100) & NULL \\
|
||
- & - & federation & Федерация & VARCHAR(100) & NULL \\
|
||
- & - & is\_registered & Зарегистрирован & BOOLEAN & NOT NULL \\
|
||
FK & \specialcell{Соревнова-\\ние.id\_Со-\\ревнование} & \specialcell{id\_competition} & \specialcell{id\_Соревнование} & INTEGER & NOT NULL \\
|
||
FK & \specialcell{Судья.\\id\_Судья} & \specialcell{id\_judge} & \specialcell{id\_Судья} & INTEGER & NOT NULL \\
|
||
\hline
|
||
\end{tabularx}
|
||
\end{table}
|
||
|
||
\begin{table}[h!]
|
||
\centering
|
||
\caption{Серия выстрелов}
|
||
\footnotesize
|
||
\begin{tabularx}{\textwidth}{|c|c|X|X|X|X|}
|
||
\hline
|
||
\textbf{KEY} & \textbf{Ссылка} & \textbf{Название EN} & \textbf{Название RU} & \textbf{Тип атрибута} & \textbf{NULL/NOT NULL} \\
|
||
\hline
|
||
PK & - & \specialcell{id\_shot\\\_series} & \specialcell{id\_Серия\\\_выстрелов} & INTEGER & NOT NULL \\
|
||
- & - & score & Очки & SMALLINT & NOT NULL \\
|
||
- & - & photo & Фото & VARCHAR(1024) & NOT NULL \\
|
||
FK & \specialcell{Заявка\_\\участника.\\id\_Заявка\_\\участника} & \specialcell{id\_participant\\\_request} & \specialcell{id\_Заявка\\\_участника} & INTEGER & NOT NULL \\
|
||
FK & \specialcell{Результат\_\\в\_этапе.id\\\_Результат\\\_в\_этапе} & \specialcell{id\_result\\\_in\_stage} & \specialcell{id\_Результат\\\_в\_этапе} & INTEGER & NOT NULL \\
|
||
FK & \specialcell{Заявка\_\\судьи.\\id\_Заявка\_\\судьи} & \specialcell{id\_judge\\\_request} & \specialcell{id\_Заявка\\\_судьи} & INTEGER & NOT NULL \\
|
||
\hline
|
||
\end{tabularx}
|
||
\end{table}
|
||
|
||
\phantom{.}
|
||
\newpage
|
||
\phantom{.}
|
||
\newpage
|
||
\phantom{.}
|
||
\newpage
|
||
\phantom{.}
|
||
\newpage
|
||
|
||
\phantom{.}
|
||
\newpage
|
||
|
||
|
||
\newpage
|
||
% Схема на русском
|
||
\includepdf[pages={1}, fitpaper, pagecommand={%
|
||
\thispagestyle{empty}%
|
||
\begin{tikzpicture}[remember picture, overlay]
|
||
\node at (current page.south) [anchor=north, yshift=35pt] {\large{Рис 6. Схема базы данных на русском}};
|
||
\end{tikzpicture}%
|
||
}]{pdf/schema_ru.pdf}
|
||
|
||
% Схема на английском
|
||
\includepdf[pages={1}, fitpaper, pagecommand={%
|
||
\thispagestyle{empty}%
|
||
\begin{tikzpicture}[remember picture, overlay]
|
||
\node at (current page.south) [anchor=north, yshift=35pt] {\large{Рис 7. Схема базы данных на английском}};
|
||
\end{tikzpicture}%
|
||
}]{pdf/schema_en.pdf}
|
||
|
||
\subsection{Схемы}
|
||
На основе аналитики предметной области, целей программы, ER-диаграммы и объектной диаграммы, была спроектирована база данных. Схема базы данных на русском языке представлена на Рис.~6, на английском языке на Рис.~7.
|
||
|
||
|
||
\subsection{Программа для заполнения базы данных}
|
||
|
||
Программа для заполнения базы данных представлена в приложении Б. Для её написания был выбран язык Python, из-за его простоты и наличия библиотеки psycopg, которая значительно упрощает взаимодействие с API PostgreSQL.
|
||
|
||
Каждой таблице в базе данных можно присвоить порядковый номер заполнения. Таблицам, которые не содержат связей с другими таблицами, то есть не содержат foreign key полей, присваивается первый порядок. Таблицам, которые содержат связи только с таблицами первого порядка, присваивается второй порядок и так далее. Порядковые номера всех таблиц и количества строк в них изображены на Рис.~6 и в таблице~\ref{count_table}.
|
||
|
||
Таблицы первого порядка заполняются осмысленными данным из составленных вручную csv таблиц. Список участников взят из настоящего соревнования. Загрузка данных из csv таблиц в указанную таблицу базы данных производится с помощью функции \texttt{load\_csv\_to\_db}.
|
||
|
||
Содержание для таблиц следующих порядков генерируется на основе таблиц первого порядка, то есть дублируемые данные переносятся из таблицы в таблицу. Дополнительные текстовые атрибуты заданы константами и одинаковы для всех строк, а дополнительные числовые атрибуты генерируются случайным образом.
|
||
|
||
Всего программа создаёт около 283 тысяч строк за примерно 50 секунд.
|
||
|
||
|
||
\begin{table}[h!]
|
||
\centering
|
||
\caption{Количества записей в таблицах базы данных}
|
||
\footnotesize
|
||
\begin{tabularx}{\textwidth}{|c|X|X|c|X|}
|
||
\hline
|
||
\textbf{Порядок} & \textbf{Название EN} & \textbf{Название RU} & \textbf{Количество} & \textbf{Комментарий} \\
|
||
\hline
|
||
1 & competition\_stage & Этап соревнования & 6 & - \\
|
||
1 & division & Дивизион & 16 & - \\
|
||
1 & organizer & Организатор & 20 & - \\
|
||
1 & judge & Судья & 50 & - \\
|
||
1 & sportsman & Спортсмен & 100 & - \\
|
||
2 & competition & Соревнование & 200 & - \\
|
||
3 & protocol & Протокол & 200 & - \\
|
||
3 & judge\_request & Заявка судьи & 1200 & От 5 до 7 на соревнование. \\
|
||
3 & \specialcell{division\_in\\\_competition} & Дивизион в соревновании & 2400 & От 8 до 16 на соревнование. \\
|
||
3 & participant\_request & Заявка участника & 9000 & От 30 до 60 на соревнование. \\
|
||
4 & result\_in\_stage & Результат в этапе & 36000 & От 2 до 6 на заявку участника. \\
|
||
5 & shot\_series & Серия выстрелов & 234000 & От 3 до 10 на результат. \\
|
||
\hline
|
||
\end{tabularx}
|
||
\label{count_table}
|
||
\end{table}
|
||
|
||
|
||
|
||
|
||
\newpage
|
||
\phantom{.}
|
||
\newpage
|
||
|
||
\section {Запросы}
|
||
\subsection{Запрос 1}
|
||
Задача: найти спортсменов, которые участвовали в соревновании А, выстрелы которых судил судья B.
|
||
|
||
Текст запроса представлен в листинге~\ref{lst:sql_1}.
|
||
|
||
\begin{lstlisting}[numbers=none, caption={Текст первого запроса}, label=lst:sql_1]
|
||
select distinct
|
||
s.id_sportsman,
|
||
s.name,
|
||
s.gender,
|
||
pr.id_competition,
|
||
jr.id_judge
|
||
from
|
||
sportsman as s
|
||
join participant_request as pr on s.id_sportsman = pr.id_sportsman
|
||
join shot_series as ss on pr.id_participant_request = ss.id_participant_request
|
||
join judge_request as jr on ss.id_judge_request = jr.id_judge_request
|
||
where
|
||
pr.id_competition = 201
|
||
and jr.id_judge = 57
|
||
\end{lstlisting}
|
||
|
||
Запрос выполняется примерно за 1 миллисекунду. Первые пять строк результата представлены на Рис.~\ref{fig:sql_1}.
|
||
|
||
\begin{figure}[h]
|
||
\centering
|
||
\includegraphics[width=1\linewidth]{img/sql_1.png}
|
||
\caption{Результат выполнения первого запроса}
|
||
\label{fig:sql_1}
|
||
\end{figure}
|
||
|
||
План выполнения запроса, постренный СУБД, представлен в текстовом виде в листинге~\ref{lst:sql_1_plan} и в графическом виде на Рис.~\ref{fig:sql_1_visual}.
|
||
|
||
По плану запроса видно, что сначала в таблице judge\_request выбираются строки по заданному id\_judge. Затем в таблице participant\_request выбираются строки по заданному id\_competition, а потом эта таблица объединяется с shot\_series по id\_participant\_request. Далее результат объединяется с judge\_request по id\_judge\_request. После чего следует последнее объединение полученной таблицы с таблицой sportsman по id\_sportsman. В конце выполняется сортировка по id\_sportsman, name и gender, для того чтобы выбрать только уникальные значения, ведь в запросе используется ключевое слово distinct.
|
||
|
||
\begin{lstlisting}[caption={План выполнения первого запроса}, label=lst:sql_1_plan]
|
||
Unique (cost=398.70..398.92 rows=22 width=57)
|
||
-> Sort (cost=398.70..398.76 rows=22 width=57)
|
||
Sort Key: s.id_sportsman, s.name, s.gender
|
||
-> Hash Join (cost=51.56..398.21 rows=22 width=57)
|
||
Hash Cond: (pr.id_sportsman = s.id_sportsman)
|
||
-> Hash Join (cost=44.31..390.90 rows=22 width=12)
|
||
Hash Cond: (ss.id_judge_request = jr.id_judge_request)
|
||
-> Nested Loop (cost=0.58..344.56 rows=993 width=12)
|
||
-> Index Scan using idx_participant_request_id_competition on participant_request pr (cost=0.29..8.96 rows=38 width=12)
|
||
Index Cond: (id_competition = 201)
|
||
-> Index Scan using idx_shot_series_id_participant_request on shot_series ss (cost=0.29..8.56 rows=27 width=8)
|
||
Index Cond: (id_participant_request = pr.id_participant_request)
|
||
-> Hash (cost=43.40..43.40 rows=26 width=8)
|
||
-> Bitmap Heap Scan on judge_request jr (cost=4.48..43.40 rows=26 width=8)
|
||
Recheck Cond: (id_judge = 57)
|
||
-> Bitmap Index Scan on idx_judge_request_id_judge (cost=0.00..4.47 rows=26 width=0)
|
||
Index Cond: (id_judge = 57)
|
||
-> Hash (cost=6.00..6.00 rows=100 width=49)
|
||
-> Seq Scan on sportsman s (cost=0.00..6.00 rows=100 width=49)
|
||
\end{lstlisting}
|
||
|
||
\begin{figure}[h]
|
||
\centering
|
||
\includegraphics[width=1\linewidth]{img/sql_1_visual.png}
|
||
\caption{Графический план выполнения первого запроса}
|
||
\label{fig:sql_1_visual}
|
||
\end{figure}
|
||
|
||
\newpage
|
||
|
||
\subsection{Запрос 2}
|
||
Задача: для соревнования А посчитать число спортсменов участвующих в нём.
|
||
|
||
Текст запроса представлен в листинге~\ref{lst:sql_2}.
|
||
|
||
\begin{lstlisting}[numbers=none, caption={Текст второго запроса}, label=lst:sql_2]
|
||
select
|
||
count(distinct id_sportsman) as count_of_participants
|
||
from
|
||
participant_request
|
||
where
|
||
id_competition = 201
|
||
\end{lstlisting}
|
||
|
||
Запрос выполняется за 100 микросекунд. Результат представлен на Рис.~\ref{fig:sql_2}.
|
||
|
||
\begin{figure}[h]
|
||
\centering
|
||
\includegraphics[width=0.4\linewidth]{img/sql_2.png}
|
||
\caption{Результат выполнения второго запроса}
|
||
\label{fig:sql_2}
|
||
\end{figure}
|
||
|
||
План выполнения запроса, постренный СУБД, представлен в текстовом виде в листинге~\ref{lst:sql_2_plan} и в графическом виде на Рис.~\ref{fig:sql_2_visual}.
|
||
|
||
Сначала в таблице participant\_request выбираются строки с заданным \\ id\_competition, а затем производится сортировка по id\_sportsman, для того чтобы выбрать только уникальные значения.
|
||
|
||
\begin{lstlisting}[caption={План выполнения второго запроса}, label=lst:sql_2_plan]
|
||
Aggregate (cost=10.15..10.16 rows=1 width=8)
|
||
-> Sort (cost=9.96..10.05 rows=38 width=4)
|
||
Sort Key: id_sportsman
|
||
-> Index Scan using idx_participant_request_id_competition on participant_request (cost=0.29..8.96 rows=38 width=4)
|
||
Index Cond: (id_competition = 201)
|
||
\end{lstlisting}
|
||
|
||
\begin{figure}[h]
|
||
\centering
|
||
\includegraphics[width=0.7\linewidth]{img/sql_2_visual.png}
|
||
\caption{Графический план выполнения второго запроса}
|
||
\label{fig:sql_2_visual}
|
||
\end{figure}
|
||
|
||
|
||
\subsection{Запрос 3}
|
||
Задача: найти дивизион, в котором участвовало больше всего спортсменов.
|
||
|
||
Текст запроса представлен в листинге~\ref{lst:sql_3}.
|
||
|
||
\begin{lstlisting}[numbers=none, caption={Текст третьего запроса}, label=lst:sql_3]
|
||
select
|
||
d.id_division,
|
||
d.title,
|
||
count(pr.name) as participant_count
|
||
from
|
||
division as d
|
||
left join participant_request as pr on d.id_division = pr.id_division
|
||
group by
|
||
d.id_division,
|
||
d.title
|
||
having
|
||
count(pr.name) = (
|
||
select
|
||
count(pr.name) as participant_count
|
||
from
|
||
division as d
|
||
left join participant_request as pr on d.id_division = pr.id_division
|
||
group by
|
||
d.id_division
|
||
order by
|
||
participant_count desc
|
||
limit 1
|
||
)
|
||
\end{lstlisting}
|
||
|
||
Запрос выполняется примерно за 9 миллисекунд. Результат представлен на Рис.~\ref{fig:sql_3}.
|
||
|
||
\begin{figure}[h]
|
||
\centering
|
||
\includegraphics[width=0.8\linewidth]{img/sql_3.png}
|
||
\caption{Результат выполнения третьего запроса}
|
||
\label{fig:sql_3}
|
||
\end{figure}
|
||
|
||
План выполнения запроса, постренный СУБД, представлен в текстовом виде в листинге~\ref{lst:sql_3_plan} и в графическом виде на Рис.~\ref{fig:sql_3_visual}.
|
||
|
||
Рассмотрим план подробнее, начиная с подзапроса. В подзапросе прежде всего происходит объединение таблиц division и participant\_request по id\_division. Далее результат объединения группируется по id\_division и сортируется по count(name). В самом конце к подзапросу применяется команда limit, которая оставляет только первую строку подзапроса. В основном запросе сначала происходит объединение таблиц participant\_request и division по id\_division, затем результат объединения группируется по id\_division. Далее применяется фильтрация, остаются только те строки, в которых count(name) равно результату подзапроса.
|
||
|
||
\begin{lstlisting}[caption={План выполнения третьего запроса}, label=lst:sql_3_plan]
|
||
HashAggregate (cost=765.70..767.57 rows=1 width=230)
|
||
Group Key: d.id_division
|
||
Filter: (count(pr.name) = $0)
|
||
InitPlan 1 (returns $0)
|
||
-> Limit (cost=383.97..383.98 rows=1 width=12)
|
||
-> Sort (cost=383.97..384.35 rows=150 width=12)
|
||
Sort Key: (count(pr_1.name)) DESC
|
||
-> HashAggregate (cost=381.72..383.22 rows=150 width=12)
|
||
Group Key: d_1.id_division
|
||
-> Hash Right Join (cost=13.38..336.14 rows=9116 width=34)
|
||
Hash Cond: (pr_1.id_division = d_1.id_division)
|
||
-> Seq Scan on participant_request pr_1 (cost=0.00..298.16 rows=9116 width=34)
|
||
-> Hash (cost=11.50..11.50 rows=150 width=4)
|
||
-> Seq Scan on division d_1 (cost=0.00..11.50 rows=150 width=4)
|
||
-> Hash Right Join (cost=13.38..336.14 rows=9116 width=252)
|
||
Hash Cond: (pr.id_division = d.id_division)
|
||
-> Seq Scan on participant_request pr (cost=0.00..298.16 rows=9116 width=34)
|
||
-> Hash (cost=11.50..11.50 rows=150 width=222)
|
||
-> Seq Scan on division d (cost=0.00..11.50 rows=150 width=222)
|
||
\end{lstlisting}
|
||
|
||
\begin{figure}[h]
|
||
\centering
|
||
\includegraphics[width=1\linewidth]{img/sql_3_visual.png}
|
||
\caption{Графический план выполнения третьего запроса}
|
||
\label{fig:sql_3_visual}
|
||
\end{figure}
|
||
|
||
|
||
\subsection{Запрос 4}
|
||
Задача: для каждого организатора посчитать количество соревнований, которые он организовал.
|
||
|
||
Текст запроса представлен в листинге~\ref{lst:sql_4}.
|
||
\newpage
|
||
|
||
\begin{lstlisting}[numbers=none, caption={Текст четвёртого запроса}, label=lst:sql_4]
|
||
select
|
||
org.id_organizer,
|
||
org.name,
|
||
count(comp.id_competition) as count_of_competitions
|
||
from
|
||
organizer as org
|
||
left join competition as comp on org.id_organizer = comp.id_organizer
|
||
group by
|
||
org.id_organizer,
|
||
org.name
|
||
\end{lstlisting}
|
||
|
||
% \begin{figure}[H]
|
||
% \centering
|
||
% \includegraphics[width=1\linewidth]{img/diagrams/sql4.png}
|
||
% \caption{Гистограмма для результатов четвёртого запроса}
|
||
% \label{fig:diag-sql4}
|
||
% \end{figure}
|
||
|
||
Запрос выполняется примерно за 2 миллисекунды. Первые пять строк результата представлены на Рис.~\ref{fig:sql_4}. Результат запроса в виде гистограммы представлен на Рис.~\ref{fig:diag-sql5}.
|
||
|
||
\begin{figure}[h]
|
||
\centering
|
||
\includegraphics[width=0.8\linewidth]{img/sql_4.png}
|
||
\caption{Результат выполнения четвёртого запроса}
|
||
\label{fig:sql_4}
|
||
\end{figure}
|
||
|
||
План выполнения запроса, постренный СУБД, представлен в текстовом виде в листинге~\ref{lst:sql_4_plan} и в графическом виде на Рис.~\ref{fig:sql_4_visual}.
|
||
|
||
По плану видно, что прежде всего выполняется объединение таблиц organizer и competition по id\_organizer, а затем выполняется группировка по тому же id\_organizer.
|
||
|
||
\begin{lstlisting}[caption={План выполнения четвёртого запроса}, label=lst:sql_4_plan]
|
||
HashAggregate (cost=10.09..10.29 rows=20 width=55)
|
||
Group Key: org.id_organizer
|
||
-> Hash Right Join (cost=1.45..9.09 rows=200 width=51)
|
||
Hash Cond: (comp.id_organizer = org.id_organizer)
|
||
-> Seq Scan on competition comp (cost=0.00..7.00 rows=200 width=8)
|
||
-> Hash (cost=1.20..1.20 rows=20 width=47)
|
||
-> Seq Scan on organizer org (cost=0.00..1.20 rows=20 width=47)
|
||
\end{lstlisting}
|
||
|
||
|
||
\begin{figure}[h]
|
||
\centering
|
||
\includegraphics[width=1\linewidth]{img/sql_4_visual.png}
|
||
\caption{Графический план выполнения четвёртого запроса}
|
||
\label{fig:sql_4_visual}
|
||
\end{figure}
|
||
|
||
\newpage
|
||
\subsection{Запрос 5}
|
||
Задача: посчитать число участников для каждого организатора.
|
||
|
||
Текст запроса представлен в листинге~\ref{lst:sql_5}.
|
||
|
||
\begin{lstlisting}[numbers=none, caption={Текст пятого запроса}, label=lst:sql_5]
|
||
select
|
||
org.id_organizer,
|
||
org.name,
|
||
count(distinct pr.id_sportsman) as count_of_participants
|
||
from
|
||
organizer as org
|
||
left join competition as comp on org.id_organizer = comp.id_organizer
|
||
left join participant_request as pr on comp.id_competition = pr.id_competition
|
||
group by
|
||
org.id_organizer,
|
||
org.name
|
||
\end{lstlisting}
|
||
|
||
Запрос выполняется за 10 миллисекунд. Первые пять строк результата представлены на Рис.~\ref{fig:sql_5}. Результат запроса в виде гистограммы представлен на Рис.~\ref{fig:diag-sql5}.
|
||
|
||
\begin{figure}[h]
|
||
\centering
|
||
\includegraphics[width=0.8\linewidth]{img/sql_5.png}
|
||
\caption{Результат выполнения пятого запроса}
|
||
\label{fig:sql_5}
|
||
\end{figure}
|
||
|
||
План выполнения запроса, постренный СУБД, представлен в текстовом виде в листинге~\ref{lst:sql_5_plan} и в графическом виде на Рис.~\ref{fig:sql_5_visual}.
|
||
|
||
По данному плану выполнения запроса видно, что сначала происходит объединение таблиц competition и participant\_request по id\_competition. Затем результат объединяется с таблицей organizer по id\_organizer. После чего производится сортировка результата по id\_organizer и id\_sportsman, чтобы выбрать оставить только уникальные значения. Последней производится группировка по id\_organizer.
|
||
|
||
\begin{lstlisting}[caption={План выполнения пятого запроса}, label=lst:sql_5_plan]
|
||
GroupAggregate (cost=962.12..1030.69 rows=20 width=55)
|
||
Group Key: org.id_organizer
|
||
-> Sort (cost=962.12..984.91 rows=9116 width=51)
|
||
Sort Key: org.id_organizer, pr.id_sportsman
|
||
-> Hash Right Join (cost=10.95..362.55 rows=9116 width=51)
|
||
Hash Cond: (comp.id_organizer = org.id_organizer)
|
||
-> Hash Right Join (cost=9.50..332.10 rows=9116 width=8)
|
||
Hash Cond: (pr.id_competition = comp.id_competition)
|
||
-> Seq Scan on participant_request pr (cost=0.00..298.16 rows=9116 width=8)
|
||
-> Hash (cost=7.00..7.00 rows=200 width=8)
|
||
-> Seq Scan on competition comp (cost=0.00..7.00 rows=200 width=8)
|
||
-> Hash (cost=1.20..1.20 rows=20 width=47)
|
||
-> Seq Scan on organizer org (cost=0.00..1.20 rows=20 width=47)
|
||
\end{lstlisting}
|
||
|
||
\begin{figure}[h]
|
||
\centering
|
||
\includegraphics[width=1\linewidth]{img/sql_5_visual.png}
|
||
\caption{Графический план выполнения пятого запроса}
|
||
\label{fig:sql_5_visual}
|
||
\end{figure}
|
||
|
||
\newpage
|
||
\begin{figure}[H]
|
||
\centering
|
||
\includegraphics[width=1\linewidth]{img/diagrams/45_sep.png}
|
||
\caption{Гистограмма для четвёртого и пятого запросов.}
|
||
\label{fig:diag-sql5}
|
||
\end{figure}
|
||
|
||
|
||
\subsection{Запрос 6}
|
||
Задача: посчитать число спортсменов с заданным числом заявок.
|
||
|
||
Текст запроса представлен в листинге~\ref{lst:sql_6}.
|
||
|
||
\begin{lstlisting}[numbers=none, caption={Текст шестого запроса}, label=lst:sql_6]
|
||
select
|
||
count_of_requests,
|
||
count(id_sportsman) as count_of_duplicates
|
||
from
|
||
(
|
||
select
|
||
s.id_sportsman,
|
||
count(pr.id_participant_request) as count_of_requests
|
||
from
|
||
sportsman as s
|
||
left join participant_request as pr on s.id_sportsman = pr.id_sportsman
|
||
group by
|
||
s.id_sportsman
|
||
)
|
||
group by
|
||
count_of_requests
|
||
\end{lstlisting}
|
||
|
||
Запрос выполняется примерно за 5 миллисекунд. Первые пять строк результата представлены на Рис.~\ref{fig:sql_6}.
|
||
|
||
\begin{figure}[h]
|
||
\centering
|
||
\includegraphics[width=0.45\linewidth]{img/sql_6.png}
|
||
\caption{Результат выполнения шестого запроса}
|
||
\label{fig:sql_6}
|
||
\end{figure}
|
||
|
||
План выполнения запроса, постренный СУБД, представлен в текстовом виде в листинге~\ref{lst:sql_6_plan} и в графическом виде на Рис.~\ref{fig:sql_6_visual}.
|
||
|
||
Рассмотрим план выполнения запроса, начиная с подзапроса. В подзапросе сначала происходит объединение таблиц sportsman и participant\_request по id\_sportsman. Затем результат группируется по id\_sportsman, при этом к столбцу id\_participant\_request применяется функция агрегации count, а получившемуся столбцу даётся псевдоним count\_of\_requests. Затем таблица сортируется по count\_of\_requests. Сортировка происходит для упрощения последующей группировки таблицы по всё тому же столбцу count\_of\_requests.
|
||
|
||
\begin{lstlisting}[caption={План выполнения шестого запроса}, label=lst:sql_6_plan]
|
||
GroupAggregate (cost=381.25..383.00 rows=100 width=16)
|
||
Group Key: unnamed_subquery.count_of_requests
|
||
-> Sort (cost=381.25..381.50 rows=100 width=12)
|
||
Sort Key: unnamed_subquery.count_of_requests
|
||
-> Subquery Scan on unnamed_subquery (cost=375.93..377.93 rows=100 width=12)
|
||
-> HashAggregate (cost=375.93..376.93 rows=100 width=12)
|
||
Group Key: s.id_sportsman
|
||
-> Hash Right Join (cost=7.25..330.35 rows=9116 width=8)
|
||
Hash Cond: (pr.id_sportsman = s.id_sportsman)
|
||
-> Seq Scan on participant_request pr (cost=0.00..298.16 rows=9116 width=8)
|
||
-> Hash (cost=6.00..6.00 rows=100 width=4)
|
||
-> Seq Scan on sportsman s (cost=0.00..6.00 rows=100 width=4)
|
||
\end{lstlisting}
|
||
\begin{figure}[h]
|
||
\centering
|
||
\includegraphics[width=1\linewidth]{img/sql_6_visual.png}
|
||
\caption{Графический план выполнения шестого запроса}
|
||
\label{fig:sql_6_visual}
|
||
\end{figure}
|
||
|
||
|
||
\subsection{Запрос 7}
|
||
Задача: найти соревнования, в которых было больше судий чем в соревновании~A.
|
||
|
||
Текст запроса представлен в листинге~\ref{lst:sql_7}.
|
||
|
||
\begin{lstlisting}[numbers=none, caption={Текст седьмого запроса}, label=lst:sql_7]
|
||
select
|
||
comp.id_competition,
|
||
comp.title,
|
||
count(jr.id_judge) as count_of_judges
|
||
from
|
||
competition as comp
|
||
join judge_request as jr on comp.id_competition = jr.id_competition
|
||
group by
|
||
comp.id_competition,
|
||
comp.title
|
||
having
|
||
count(jr.id_judge) > (
|
||
select
|
||
count(*)
|
||
from
|
||
judge_request
|
||
where
|
||
id_competition = 250
|
||
)
|
||
\end{lstlisting}
|
||
|
||
Запрос выполняется примерно за 1 миллисекунду. Первые пять строк результата представлены на Рис.~\ref{fig:sql_7}.
|
||
|
||
\begin{figure}[h]
|
||
\centering
|
||
\includegraphics[width=0.7\linewidth]{img/sql_7.png}
|
||
\caption{Результат выполнения седьмого запроса}
|
||
\label{fig:sql_7}
|
||
\end{figure}
|
||
|
||
План выполнения запроса, постренный СУБД, представлен в текстовом виде в листинге~\ref{lst:sql_7_plan} и в графическом виде на Рис.~\ref{fig:sql_7_visual}.
|
||
|
||
Рассмотрим план подробнее, начиная с подзапроса. В подзапросе прежде всего из таблицы judge\_request выбираются строки с указанным id\_competition, а затем подсчитывается общее количество полученных строк. В основном запросе сначала происходит объединение таблиц competition и judge\_request по id\_competition, затем результат объединения группируется по id\_competition. Далее применяется фильтрация, остаются только те строки, в которых count(id\_judge) строго больше результата подзапроса.
|
||
\newpage
|
||
\begin{lstlisting}[caption={План выполнения седьмого запроса}, label=lst:sql_7_plan]
|
||
HashAggregate (cost=77.11..79.61 rows=67 width=30)
|
||
Group Key: comp.id_competition
|
||
Filter: (count(jr.id_judge) > $0)
|
||
InitPlan 1 (returns $0)
|
||
-> Aggregate (cost=4.40..4.41 rows=1 width=8)
|
||
-> Index Only Scan using idx_judge_request_id_competition on judge_request (cost=0.28..4.38 rows=6 width=0)
|
||
Index Cond: (id_competition = 250)
|
||
-> Hash Join (cost=9.50..66.70 rows=1199 width=26)
|
||
Hash Cond: (jr.id_competition = comp.id_competition)
|
||
-> Seq Scan on judge_request jr (cost=0.00..53.99 rows=1199 width=8)
|
||
-> Hash (cost=7.00..7.00 rows=200 width=22)
|
||
-> Seq Scan on competition comp (cost=0.00..7.00 rows=200 width=22)
|
||
\end{lstlisting}
|
||
|
||
\begin{figure}[H]
|
||
\centering
|
||
\includegraphics[width=0.8\linewidth]{img/sql_7_visual.png}
|
||
\caption{Графический план выполнения седьмого запроса}
|
||
\label{fig:sql_7_visual}
|
||
\end{figure}
|
||
|
||
|
||
\subsection{Запрос 8}
|
||
Задача: найти судей, которые никогда не судили ничего в дивизионе А.
|
||
|
||
Текст запроса представлен в листинге~\ref{lst:sql_8}.
|
||
|
||
\begin{lstlisting}[numbers=none, caption={Текст восьмого запроса}, label=lst:sql_8]
|
||
select
|
||
*
|
||
from
|
||
judge
|
||
where
|
||
id_judge not in (
|
||
select
|
||
j.id_judge
|
||
from
|
||
judge as j
|
||
left join judge_request as jr on j.id_judge = jr.id_judge
|
||
left join shot_series as ss on jr.id_judge_request = ss.id_judge_request
|
||
left join result_in_stage as ris on ss.id_result_in_stage = ris.id_result_in_stage
|
||
where
|
||
ris.id_division = 20
|
||
)
|
||
\end{lstlisting}
|
||
|
||
Запрос выполняется за 40 миллисекунд. Результат представлены на Рис.~\ref{fig:sql_8}.
|
||
|
||
\begin{figure}[h]
|
||
\centering
|
||
\includegraphics[width=1\linewidth]{img/sql_8.png}
|
||
\caption{Результат выполнения восьмого запроса}
|
||
\label{fig:sql_8}
|
||
\end{figure}
|
||
|
||
План выполнения запроса, постренный СУБД, представлен в текстовом виде в листинге~\ref{lst:sql_8_plan} и в графическом виде на Рис.~\ref{fig:sql_8_visual}.
|
||
|
||
Рассмотрим план подробнее, начиная с подзапроса. В подзапросе прежде всего из таблицы result\_in\_stage выбираются строки с указанным id\_division, затем отфильтрованный result\_in\_stage объединяется с таблицей shot\_series по id\_result\_in\_stage. Затем результат объединяется с таблицей judge\_request по id\_judge\_request. После чего идёт последнее объединение с таблицей judge по id\_judge. В основном запросе происходит фильтрация строк таблицы judge с условием not и результатом подзапроса. Это означает, что выбираются только те строки id\_judge, которых нет в сохранённом результате подзапроса.
|
||
|
||
\begin{lstlisting}[caption={План выполнения восьмого запроса}, label=lst:sql_8_plan]
|
||
Seq Scan on judge (cost=5448.30..5450.92 rows=25 width=101)
|
||
Filter: (NOT (hashed SubPlan 1))
|
||
SubPlan 1
|
||
-> Hash Join (cost=367.78..5421.81 rows=10596 width=4)
|
||
Hash Cond: (jr.id_judge = j.id_judge)
|
||
-> Hash Join (cost=364.65..5388.51 rows=10596 width=4)
|
||
Hash Cond: (ss.id_judge_request = jr.id_judge_request)
|
||
-> Hash Join (cost=295.67..5291.62 rows=10596 width=4)
|
||
Hash Cond: (ss.id_result_in_stage = ris.id_result_in_stage)
|
||
-> Seq Scan on shot_series ss (cost=0.00..4370.26 rows=238326 width=8)
|
||
-> Hash (cost=275.30..275.30 rows=1630 width=4)
|
||
-> Bitmap Heap Scan on result_in_stage ris (cost=20.92..275.30 rows=1630 width=4)
|
||
Recheck Cond: (id_division = 20)
|
||
-> Bitmap Index Scan on idx_result_in_stage_id_division (cost=0.00..20.52 rows=1630 width=0)
|
||
Index Cond: (id_division = 20)
|
||
-> Hash (cost=53.99..53.99 rows=1199 width=8)
|
||
-> Seq Scan on judge_request jr (cost=0.00..53.99 rows=1199 width=8)
|
||
-> Hash (cost=2.50..2.50 rows=50 width=4)
|
||
-> Seq Scan on judge j (cost=0.00..2.50 rows=50 width=4)
|
||
\end{lstlisting}
|
||
|
||
\begin{figure}[h]
|
||
\centering
|
||
\includegraphics[width=1\linewidth]{img/sql_8_visual.png}
|
||
\caption{Графический план выполнения восьмого запроса}
|
||
\label{fig:sql_8_visual}
|
||
\end{figure}
|
||
|
||
|
||
\subsection{Запрос 9}
|
||
Задача: для случайных 25 спортсменов и каждого организатора посчитать число участия в одном и том же соревновании.
|
||
|
||
Текст запроса представлен в листинге~\ref{lst:sql_9}.
|
||
|
||
\begin{lstlisting}[numbers=none, caption={Текст девятого запроса}, label=lst:sql_9]
|
||
select
|
||
o.id_organizer,
|
||
o.name,
|
||
s.id_sportsman,
|
||
s.name,
|
||
(
|
||
select
|
||
count(distinct comp.id_competition)
|
||
from
|
||
competition as comp
|
||
join participant_request as pr on comp.id_competition = pr.id_competition
|
||
where
|
||
comp.id_organizer = o.id_organizer
|
||
and pr.id_sportsman = s.id_sportsman
|
||
)
|
||
from
|
||
organizer as o
|
||
cross join (
|
||
select
|
||
*
|
||
from
|
||
sportsman
|
||
order by
|
||
random ()
|
||
limit
|
||
25
|
||
) as s
|
||
\end{lstlisting}
|
||
|
||
Запрос выполняется за 71 миллисекунд. Первые пять строк результата представлены на Рис.~\ref{fig:sql_9}. Результат в виде диаграммы представлен на Рис.~\ref{fig:sql_9_gist}.
|
||
|
||
\begin{figure}[h]
|
||
\centering
|
||
\includegraphics[width=0.8\linewidth]{img/sql_9.png}
|
||
\caption{Результат выполнения девятого запроса}
|
||
\label{fig:sql_9}
|
||
\end{figure}
|
||
|
||
План выполнения запроса, постренный СУБД, представлен в текстовом виде в листинге~\ref{lst:sql_9_plan} и в графическом виде на Рис.~\ref{fig:sql_9_visual}.
|
||
|
||
|
||
Рассмотрим план подробнее, начиная с подзапроса внутри select. В подзапросе прежде всего из таблицы competition выбираются строки с заданным id\_organizer. Затем результат объединяется с таблицей participant\_request по id\_competition, при этом в participant\_request выбираются строки с заданным id\_sportsman. Далее результат сортируется по id\_competition, для того чтобы оставить только уникальные значения, и агрегируются, чтобы посчитать количество строк. В запросе есть ещё один подзапрос внутри from, в нём просто считываются строки таблицы sportsman, затем сортируются случайным образом с помощью функции random(), а затем применяется команда limit, чтобы выбрать первые 25 строк. В основном происходит объединение таблиц с помощью Nested Loop, при этом первой подзапрос выполняется в цикле и формируется финальная таблица.
|
||
|
||
|
||
\begin{lstlisting}[caption={План выполнения девятого запроса}, label=lst:sql_9_plan]
|
||
Nested Loop (cost=9.07..63916.76 rows=500 width=89)
|
||
-> Limit (cost=9.07..9.13 rows=25 width=956)
|
||
-> Sort (cost=9.07..9.32 rows=100 width=956)
|
||
Sort Key: (random())
|
||
-> Seq Scan on sportsman (cost=0.00..6.25 rows=100 width=956)
|
||
-> Materialize (cost=0.00..1.30 rows=20 width=47)
|
||
-> Seq Scan on organizer o (cost=0.00..1.20 rows=20 width=47)
|
||
SubPlan 1
|
||
-> Aggregate (cost=127.79..127.80 rows=1 width=8)
|
||
-> Sort (cost=127.76..127.78 rows=5 width=4)
|
||
Sort Key: comp.id_competition
|
||
-> Nested Loop (cost=0.29..127.71 rows=5 width=4)
|
||
-> Seq Scan on competition comp (cost=0.00..7.50 rows=10 width=4)
|
||
Filter: (id_organizer = o.id_organizer)
|
||
-> Index Scan using idx_participant_request_id_competition on participant_request pr (cost=0.29..12.01 rows=1 width=4)
|
||
Index Cond: (id_competition = comp.id_competition)
|
||
Filter: (id_sportsman = sportsman.id_sportsman)
|
||
\end{lstlisting}
|
||
|
||
\begin{figure}[h]
|
||
\centering
|
||
\includegraphics[width=1\linewidth]{img/sql_9_visual.png}
|
||
\caption{Графический план выполнения девятого запроса}
|
||
\label{fig:sql_9_visual}
|
||
\end{figure}
|
||
|
||
\newpage
|
||
\begin{figure}[H]
|
||
\centering
|
||
\hspace*{-0.125\linewidth}
|
||
\includegraphics[width=1.25\linewidth]{img/diagrams/sql9.png}
|
||
\caption{Гистограмма построенная на основе результатов выполнения девятого запроса}
|
||
\label{fig:sql_9_gist}
|
||
\end{figure}
|
||
|
||
|
||
|
||
\newpage
|
||
\section* {Заключение}
|
||
|
||
\addcontentsline{toc}{section}{Заключение}
|
||
Для выполнения этой работы был проведён анализ выбранной предметной области -- стрельбы из лука. На его основе были построены ER-диаграмма, диаграмма объектов и схема база данных на русском и английском языках. Затем по полученным схемам и диаграммам были составлены таблицы метаданных.
|
||
|
||
Для реализации самой базы данных была выбрана система управления базами данных PostgreSQL. По таблицам метаданных на языке SQL были написаны и выполнены команды для создания необходимых таблиц и самой базы данных. Также был на языке Python был написан скрипт для автоматического заполнения базы данных, состоящий из примерно пятисот строк. По резлутатом его работы в базе данных было создано около 280,000 тысяч строк.
|
||
|
||
Далее были на русском языке были сформулированы девять различных запросов к базе данных. Они были переведены в запросы на языке SQL. Время выполнения и результаты их работы отражены в отчёте.
|
||
|
||
На выполнение первого этапа работ было потрачено около 15 часов, второго около 10 часов, третьего около 15 часов.
|
||
|
||
% \newpage
|
||
% \section*{Список литературы}
|
||
% \addcontentsline{toc}{section}{Список литературы}
|
||
|
||
% \vspace{-1.5cm}
|
||
% \begin{thebibliography}{0}
|
||
% \bibitem{1}
|
||
% Д. Кук, Г. Бейз, <<Компьютерная математика>>. — 1-е изд. — Москва: Наука, 1990. — 383 с.
|
||
% \bibitem{2}
|
||
% Новиков, Ф. А. <<Дискретная математика для программистов>>. — 3-е изд. — Санкт-Петербург: Питер, 2009. — 383 с.
|
||
% \bibitem{3}
|
||
% Полубенцева, М. И. <<Объектно-ориентированное программирование C++>>. — 1-е изд. — Санкт-Петербург: БХВ-Петербург, 2008. — 448 с.
|
||
|
||
% \end{thebibliography}
|
||
|
||
|
||
|
||
|
||
|
||
% \lstset{
|
||
% backgroundcolor=\color{white},
|
||
% frame=single
|
||
% }
|
||
|
||
\newpage
|
||
\addcontentsline{toc}{section}{Приложение А. Текст создания БД}
|
||
\section*{Приложение А. Текст создания БД}
|
||
\begin{lstlisting}[label=A]
|
||
CREATE DATABASE db_paradox;
|
||
\c db_paradox;
|
||
|
||
CREATE TABLE organizer (
|
||
id_organizer SERIAL PRIMARY KEY,
|
||
name VARCHAR(100) NOT NULL,
|
||
logo VARCHAR(1024)
|
||
);
|
||
|
||
CREATE TABLE judge (
|
||
id_judge SERIAL PRIMARY KEY,
|
||
name VARCHAR(100) NOT NULL,
|
||
category VARCHAR(100) NOT NULL,
|
||
region VARCHAR(100),
|
||
federation VARCHAR(100)
|
||
);
|
||
|
||
CREATE TABLE sportsman (
|
||
id_sportsman SERIAL PRIMARY KEY,
|
||
name VARCHAR(100) NOT NULL,
|
||
gender VARCHAR(10) NOT NULL,
|
||
birthday DATE,
|
||
region VARCHAR(100),
|
||
club VARCHAR(100),
|
||
federation VARCHAR(100),
|
||
category VARCHAR(100)
|
||
);
|
||
|
||
CREATE TABLE division (
|
||
id_division SERIAL PRIMARY KEY,
|
||
title VARCHAR(100) NOT NULL,
|
||
gender VARCHAR(10) NOT NULL,
|
||
bow_type VARCHAR(100) NOT NULL,
|
||
distance SMALLINT NOT NULL,
|
||
msmk SMALLINT NOT NULL,
|
||
ms SMALLINT NOT NULL,
|
||
kms SMALLINT NOT NULL,
|
||
category_1 SMALLINT NOT NULL,
|
||
category_2 SMALLINT NOT NULL,
|
||
category_3 SMALLINT NOT NULL
|
||
);
|
||
|
||
CREATE TABLE competition (
|
||
id_competition SERIAL PRIMARY KEY,
|
||
title VARCHAR(100) NOT NULL,
|
||
address VARCHAR(100) NOT NULL,
|
||
logo VARCHAR(1024),
|
||
start_date DATE NOT NULL,
|
||
end_date DATE NOT NULL,
|
||
id_organizer INTEGER NOT NULL,
|
||
FOREIGN KEY (id_organizer) REFERENCES organizer(id_organizer)
|
||
);
|
||
CREATE INDEX idx_competition_id_organizer ON competition(id_organizer);
|
||
|
||
CREATE TABLE division_in_competition (
|
||
id_division_in_competition SERIAL PRIMARY KEY,
|
||
id_division INTEGER NOT NULL,
|
||
id_competition INTEGER NOT NULL,
|
||
FOREIGN KEY (id_division) REFERENCES division(id_division),
|
||
FOREIGN KEY (id_competition) REFERENCES competition(id_competition)
|
||
);
|
||
CREATE INDEX idx_division_in_competition_id_division ON division_in_competition(id_division);
|
||
CREATE INDEX idx_division_in_competition_id_competition ON division_in_competition(id_competition);
|
||
|
||
CREATE TABLE protocol (
|
||
id_protocol SERIAL PRIMARY KEY,
|
||
name VARCHAR(100) NOT NULL,
|
||
date DATE NOT NULL,
|
||
file VARCHAR(1024) NOT NULL,
|
||
id_competition INTEGER NOT NULL,
|
||
FOREIGN KEY (id_competition) REFERENCES competition(id_competition)
|
||
);
|
||
CREATE INDEX idx_protocol_id_competition ON protocol(id_competition);
|
||
|
||
CREATE TABLE participant_request (
|
||
id_participant_request SERIAL PRIMARY KEY,
|
||
name VARCHAR(100) NOT NULL,
|
||
gender VARCHAR(10) NOT NULL,
|
||
birthday DATE NOT NULL,
|
||
region VARCHAR(100),
|
||
club VARCHAR(100),
|
||
federation VARCHAR(100),
|
||
category VARCHAR(100) NOT NULL,
|
||
is_registered BOOLEAN NOT NULL,
|
||
id_competition INTEGER NOT NULL,
|
||
id_sportsman INTEGER NOT NULL,
|
||
id_division INTEGER NOT NULL,
|
||
FOREIGN KEY (id_competition) REFERENCES competition(id_competition),
|
||
FOREIGN KEY (id_sportsman) REFERENCES sportsman(id_sportsman),
|
||
FOREIGN KEY (id_division) REFERENCES division(id_division)
|
||
);
|
||
CREATE INDEX idx_participant_request_id_competition ON participant_request(id_competition);
|
||
CREATE INDEX idx_participant_request_id_sportsman ON participant_request(id_sportsman);
|
||
CREATE INDEX idx_participant_request_id_division ON participant_request(id_division);
|
||
|
||
CREATE TABLE competition_stage (
|
||
id_competition_stage SERIAL PRIMARY KEY,
|
||
title VARCHAR(100) NOT NULL,
|
||
count_of_series SMALLINT NOT NULL
|
||
);
|
||
|
||
CREATE TABLE result_in_stage (
|
||
id_result_in_stage SERIAL PRIMARY KEY,
|
||
score SMALLINT NOT NULL,
|
||
shield_index SMALLINT NOT NULL,
|
||
target_index CHAR(1) NOT NULL,
|
||
id_participant_request INTEGER NOT NULL,
|
||
id_division INTEGER NOT NULL,
|
||
id_competition_stage INTEGER NOT NULL,
|
||
FOREIGN KEY (id_participant_request) REFERENCES participant_request(id_participant_request),
|
||
FOREIGN KEY (id_division) REFERENCES division(id_division),
|
||
FOREIGN KEY (id_competition_stage) REFERENCES competition_stage(id_competition_stage)
|
||
);
|
||
CREATE INDEX idx_result_in_stage_id_participant_request ON result_in_stage(id_participant_request);
|
||
CREATE INDEX idx_result_in_stage_id_division ON result_in_stage(id_division);
|
||
CREATE INDEX idx_result_in_stage_id_competition_stage ON result_in_stage(id_competition_stage);
|
||
|
||
CREATE TABLE judge_request (
|
||
id_judge_request SERIAL PRIMARY KEY,
|
||
name VARCHAR(100) NOT NULL,
|
||
category VARCHAR(100) NOT NULL,
|
||
region VARCHAR(100),
|
||
federation VARCHAR(100),
|
||
is_registered BOOLEAN NOT NULL,
|
||
id_competition INTEGER NOT NULL,
|
||
id_judge INTEGER NOT NULL,
|
||
FOREIGN KEY (id_competition) REFERENCES competition(id_competition),
|
||
FOREIGN KEY (id_judge) REFERENCES judge(id_judge)
|
||
);
|
||
CREATE INDEX idx_judge_request_id_competition ON judge_request(id_competition);
|
||
CREATE INDEX idx_judge_request_id_judge ON judge_request(id_judge);
|
||
|
||
CREATE TABLE shot_series (
|
||
id_shot_series SERIAL PRIMARY KEY,
|
||
score INTEGER NOT NULL,
|
||
photo VARCHAR(1024) NOT NULL,
|
||
id_participant_request INTEGER NOT NULL,
|
||
id_result_in_stage INTEGER NOT NULL,
|
||
id_judge_request INTEGER NOT NULL,
|
||
FOREIGN KEY (id_participant_request) REFERENCES participant_request(id_participant_request),
|
||
FOREIGN KEY (id_result_in_stage) REFERENCES result_in_stage(id_result_in_stage),
|
||
FOREIGN KEY (id_judge_request) REFERENCES judge_request(id_judge_request)
|
||
);
|
||
CREATE INDEX idx_shot_series_id_participant_request ON shot_series(id_participant_request);
|
||
CREATE INDEX idx_shot_series_id_result_in_stage ON shot_series(id_result_in_stage);
|
||
CREATE INDEX idx_shot_series_id_judge_request ON shot_series(id_judge_request);
|
||
\end{lstlisting}
|
||
|
||
\newpage
|
||
\addcontentsline{toc}{section}{Приложение Б. Текст программы для заполнения БД}
|
||
\section*{Приложение Б. Текст программы для заполнения БД}
|
||
\addcontentsline{toc}{subsection}{Приложение Б.1. Файл main.py}
|
||
\subsection*{Приложение Б.1. Файл main.py}
|
||
\begin{lstlisting}[label=B1]
|
||
import time
|
||
|
||
from utils import *
|
||
|
||
# Параметры подключения к базе данных
|
||
conn_params = {
|
||
"dbname": "db_univer",
|
||
"user": "postgres",
|
||
"password": "123",
|
||
"host": "localhost",
|
||
}
|
||
|
||
|
||
if __name__ == "__main__":
|
||
delete_all_rows_from_all_tables(conn_params)
|
||
|
||
start_time = time.perf_counter()
|
||
|
||
# Заполняем таблицы первого уровня
|
||
load_csv_to_db(conn_params, "division", "division.csv")
|
||
load_csv_to_db(conn_params, "organizer", "organizer.csv")
|
||
load_csv_to_db(conn_params, "competition_stage", "competition_stage.csv")
|
||
load_csv_to_db(conn_params, "sportsman", "sportsman.csv", date_columns=[2])
|
||
load_csv_to_db(conn_params, "judge", "judge.csv")
|
||
|
||
# Второй уровень
|
||
generate_competitions(conn_params, 200)
|
||
|
||
# Третий уровень
|
||
generate_protocols(conn_params)
|
||
generate_judge_requests(conn_params)
|
||
generate_participant_requests(conn_params)
|
||
generate_division_in_competitions(conn_params)
|
||
|
||
# Четвёртый уровень
|
||
generate_result_in_stage(conn_params)
|
||
|
||
# Пятый уровень
|
||
generate_shot_series(conn_params)
|
||
|
||
print_rows_count_summary(conn_params)
|
||
|
||
print(f"База данных заполнена за {time.perf_counter() - start_time:.3}с")
|
||
\end{lstlisting}
|
||
|
||
\newpage
|
||
\addcontentsline{toc}{subsection}{Приложение Б.2. Файл utils.py}
|
||
\subsection*{Приложение Б.2. Файл utils.py}
|
||
\begin{lstlisting}[label=B2]
|
||
import csv
|
||
import random
|
||
from datetime import datetime, timedelta
|
||
|
||
import psycopg2
|
||
|
||
|
||
def load_csv_to_db(
|
||
conn_params, table_name, csv_file_path, date_columns=None, delimiter=";"
|
||
):
|
||
"""Добавляет данные из csv таблицы в указанную таблицу в бд. Важно, чтобы названия
|
||
столбцов и типы данных в таблицах совпадали. Преобразует даты в указанных столбцах.
|
||
"""
|
||
# Соединение с базой данных
|
||
with psycopg2.connect(**conn_params) as conn:
|
||
with conn.cursor() as cur:
|
||
# Открытие CSV-файла
|
||
with open(csv_file_path, mode="r", encoding="windows-1251") as file:
|
||
reader = csv.reader(file, delimiter=delimiter)
|
||
# Считывание заголовков для формирования SQL запроса
|
||
headers = next(reader)
|
||
|
||
# Формирование строки запроса с плейсхолдерами для значений
|
||
placeholders = ", ".join(["%s"] * len(headers))
|
||
insert_query = f"INSERT INTO {table_name} ({', '.join(headers)}) VALUES ({placeholders});"
|
||
|
||
# Вставка данных в таблицу
|
||
for row in reader:
|
||
# Преобразование дат, если требуется
|
||
if date_columns:
|
||
for col_index in date_columns:
|
||
if row[col_index]: # Проверяем, не пустое ли значение
|
||
row[col_index] = datetime.strptime(
|
||
row[col_index], "%d.%m.%Y"
|
||
).strftime("%Y-%m-%d")
|
||
cur.execute(insert_query, row)
|
||
conn.commit()
|
||
|
||
|
||
def print_rows_count_summary(conn_params):
|
||
"""Выводит в консолько количество строк во всех таблицах в базе данных, а также
|
||
выводит суммарное количество всех строк в базе данных."""
|
||
total_rows = 0
|
||
with psycopg2.connect(**conn_params) as conn:
|
||
with conn.cursor() as cur:
|
||
# Получение списка всех таблиц в текущей базе данных
|
||
cur.execute(
|
||
"""
|
||
SELECT table_name
|
||
FROM information_schema.tables
|
||
WHERE table_schema = 'public'
|
||
"""
|
||
)
|
||
tables = cur.fetchall()
|
||
|
||
# Подсчет строк в каждой таблице
|
||
for (table_name,) in tables:
|
||
cur.execute(f"SELECT COUNT(*) FROM {table_name}")
|
||
count = cur.fetchone()[0]
|
||
total_rows += count
|
||
print(f"Таблица {table_name}: {count} строк")
|
||
|
||
print("Всего строк в базе данных: ", total_rows)
|
||
print()
|
||
|
||
|
||
def delete_all_rows_from_all_tables(conn_params):
|
||
"""Очищает базу данных. Удаляет все строки из всех таблиц, но оставляет
|
||
сами таблицы."""
|
||
with psycopg2.connect(**conn_params) as conn:
|
||
with conn.cursor() as cur:
|
||
# Отключение ограничений foreign key для избежания конфликтов при удалении
|
||
cur.execute("SET session_replication_role = 'replica';")
|
||
|
||
# Получение списка всех таблиц в текущей базе данных
|
||
cur.execute(
|
||
"""
|
||
SELECT table_name
|
||
FROM information_schema.tables
|
||
WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
|
||
"""
|
||
)
|
||
tables = cur.fetchall()
|
||
|
||
# Удаление всех строк из каждой таблицы
|
||
for (table_name,) in tables:
|
||
cur.execute(f"DELETE FROM {table_name}")
|
||
print(f"Все строки удалены из таблицы {table_name}")
|
||
|
||
# Восстановление ограничений foreign key
|
||
cur.execute("SET session_replication_role = 'origin';")
|
||
|
||
# Подтверждение всех изменений
|
||
conn.commit()
|
||
|
||
print()
|
||
|
||
|
||
def generate_competitions(conn_params, num_records):
|
||
with psycopg2.connect(**conn_params) as conn:
|
||
with conn.cursor() as cur:
|
||
# Получение списка ID организаторов
|
||
cur.execute("SELECT id_organizer FROM organizer")
|
||
organizer_ids = [row[0] for row in cur.fetchall()]
|
||
|
||
for _ in range(num_records):
|
||
# Генерация случайного организатора
|
||
id_organizer = random.choice(organizer_ids)
|
||
|
||
# Генерация случайных дат
|
||
start_date = datetime.now() - timedelta(days=random.randint(1, 5 * 365))
|
||
end_date = start_date + timedelta(days=random.randint(1, 10))
|
||
|
||
# Форматирование дат для SQL
|
||
start_date = start_date.strftime("%Y-%m-%d")
|
||
end_date = end_date.strftime("%Y-%m-%d")
|
||
|
||
# Вставка данных в таблицу competition
|
||
cur.execute(
|
||
"""
|
||
INSERT INTO competition (title, address, logo, start_date, end_date, id_organizer)
|
||
VALUES (%s, %s, %s, %s, %s, %s);
|
||
""",
|
||
(
|
||
"Competition Title",
|
||
"Competition Address",
|
||
"Competition Logo",
|
||
start_date,
|
||
end_date,
|
||
id_organizer,
|
||
),
|
||
)
|
||
|
||
conn.commit()
|
||
|
||
|
||
def generate_protocols(conn_params):
|
||
with psycopg2.connect(**conn_params) as conn:
|
||
with conn.cursor() as cur:
|
||
# Получение ID соревнований, которые еще не имеют протоколов
|
||
cur.execute(
|
||
"""
|
||
SELECT id_competition FROM competition
|
||
WHERE id_competition NOT IN (SELECT id_competition FROM protocol)
|
||
"""
|
||
)
|
||
competition_ids = [row[0] for row in cur.fetchall()]
|
||
|
||
for comp_id in competition_ids:
|
||
# Генерация случайной даты и имени файла
|
||
date = datetime.now() - timedelta(days=random.randint(1, 5 * 365))
|
||
date = date.strftime("%Y-%m-%d")
|
||
file_name = f"protocol_{comp_id}.pdf"
|
||
|
||
# Вставка данных в таблицу protocol
|
||
cur.execute(
|
||
"""
|
||
INSERT INTO protocol (name, date, file, id_competition)
|
||
VALUES (%s, %s, %s, %s);
|
||
""",
|
||
(f"Protocol for Competition {comp_id}", date, file_name, comp_id),
|
||
)
|
||
|
||
conn.commit()
|
||
|
||
|
||
def generate_judge_requests(conn_params):
|
||
with psycopg2.connect(**conn_params) as conn:
|
||
with conn.cursor() as cur:
|
||
# Получение списка всех соревнований
|
||
cur.execute("SELECT id_competition FROM competition")
|
||
competitions = [row[0] for row in cur.fetchall()]
|
||
|
||
# Получение списка всех судей
|
||
cur.execute(
|
||
"SELECT id_judge, name, category, region, federation FROM judge"
|
||
)
|
||
judges = cur.fetchall()
|
||
|
||
for competition_id in competitions:
|
||
# Выбор случайного количества судей от 5 до 7 для каждого соревнования
|
||
selected_judges = random.sample(judges, random.randint(5, 7))
|
||
|
||
for judge in selected_judges:
|
||
id_judge, name, category, region, federation = judge
|
||
# Определение статуса регистрации
|
||
is_registered = random.choices([True, False], [0.9, 0.1])[0]
|
||
|
||
# Вставка данных в таблицу judge_request
|
||
cur.execute(
|
||
"""
|
||
INSERT INTO judge_request (name, category, region, federation, is_registered, id_competition, id_judge)
|
||
VALUES (%s, %s, %s, %s, %s, %s, %s);
|
||
""",
|
||
(
|
||
name,
|
||
category,
|
||
region,
|
||
federation,
|
||
is_registered,
|
||
competition_id,
|
||
id_judge,
|
||
),
|
||
)
|
||
|
||
conn.commit()
|
||
|
||
|
||
def generate_participant_requests(conn_params):
|
||
with psycopg2.connect(**conn_params) as conn:
|
||
with conn.cursor() as cur:
|
||
# Получение списка всех соревнований
|
||
cur.execute("SELECT id_competition FROM competition")
|
||
competitions = [row[0] for row in cur.fetchall()]
|
||
|
||
# Получение списка всех спортсменов
|
||
cur.execute(
|
||
"SELECT id_sportsman, name, gender, birthday, region, club, federation, category FROM sportsman"
|
||
)
|
||
sportsmen = cur.fetchall()
|
||
|
||
for competition_id in competitions:
|
||
# Выбор случайного количества участников
|
||
selected_sportsmen = random.sample(sportsmen, random.randint(30, 60))
|
||
|
||
for sportsman in selected_sportsmen:
|
||
(
|
||
id_sportsman,
|
||
name,
|
||
gender,
|
||
birthday,
|
||
region,
|
||
club,
|
||
federation,
|
||
category,
|
||
) = sportsman
|
||
# Определение статуса регистрации
|
||
is_registered = random.choices([True, False], [0.9, 0.1])[0]
|
||
|
||
# Выбор подходящих дивизионов с учетом гендера
|
||
cur.execute(
|
||
"""
|
||
SELECT id_division FROM division
|
||
WHERE gender = %s
|
||
""",
|
||
(gender,),
|
||
)
|
||
divisions = [row[0] for row in cur.fetchall()]
|
||
|
||
# Выбор случайного дивизиона из подходящих
|
||
id_division = random.choice(divisions)
|
||
|
||
# Вставка данных в таблицу participant_request
|
||
cur.execute(
|
||
"""
|
||
INSERT INTO participant_request (
|
||
name, gender, birthday, region, club, federation, category, is_registered, id_competition, id_sportsman, id_division
|
||
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s);
|
||
""",
|
||
(
|
||
name,
|
||
gender,
|
||
birthday,
|
||
region,
|
||
club,
|
||
federation,
|
||
category,
|
||
is_registered,
|
||
competition_id,
|
||
id_sportsman,
|
||
id_division,
|
||
),
|
||
)
|
||
|
||
conn.commit()
|
||
|
||
|
||
def generate_division_in_competitions(conn_params):
|
||
with psycopg2.connect(**conn_params) as conn:
|
||
with conn.cursor() as cur:
|
||
# Получение списка всех соревнований
|
||
cur.execute("SELECT id_competition FROM competition")
|
||
competitions = [row[0] for row in cur.fetchall()]
|
||
|
||
# Получение списка всех дивизионов
|
||
cur.execute("SELECT id_division FROM division")
|
||
divisions = [row[0] for row in cur.fetchall()]
|
||
|
||
for competition_id in competitions:
|
||
# Выбор случайного количества дивизионов
|
||
selected_divisions = random.sample(divisions, random.randint(8, 16))
|
||
|
||
for division_id in selected_divisions:
|
||
# Вставка данных в таблицу division_in_competition
|
||
cur.execute(
|
||
"""
|
||
INSERT INTO division_in_competition (id_division, id_competition)
|
||
VALUES (%s, %s);
|
||
""",
|
||
(division_id, competition_id),
|
||
)
|
||
|
||
conn.commit()
|
||
|
||
|
||
def generate_result_in_stage(conn_params):
|
||
with psycopg2.connect(**conn_params) as conn:
|
||
with conn.cursor() as cur:
|
||
# Получение списка всех заявок участников
|
||
cur.execute(
|
||
"""
|
||
SELECT id_participant_request, id_division
|
||
FROM participant_request
|
||
"""
|
||
)
|
||
participant_requests = cur.fetchall()
|
||
|
||
# Получение списка всех этапов соревнований
|
||
cur.execute("SELECT id_competition_stage FROM competition_stage")
|
||
competition_stages = [row[0] for row in cur.fetchall()]
|
||
|
||
for request in participant_requests:
|
||
id_participant_request, id_division = request
|
||
# Количество результатов для каждой заявки
|
||
num_results = random.randint(2, 6)
|
||
|
||
for _ in range(num_results):
|
||
score = random.randint(150, 300)
|
||
shield_index = random.randint(0, 20)
|
||
target_index = random.choice(["A", "B", "C", "D"])
|
||
id_competition_stage = random.choice(competition_stages)
|
||
|
||
# Вставка данных в таблицу result_in_stage
|
||
cur.execute(
|
||
"""
|
||
INSERT INTO result_in_stage (score, shield_index, target_index, id_participant_request, id_division, id_competition_stage)
|
||
VALUES (%s, %s, %s, %s, %s, %s);
|
||
""",
|
||
(
|
||
score,
|
||
shield_index,
|
||
target_index,
|
||
id_participant_request,
|
||
id_division,
|
||
id_competition_stage,
|
||
),
|
||
)
|
||
|
||
conn.commit()
|
||
|
||
|
||
def generate_shot_series(conn_params):
|
||
with psycopg2.connect(**conn_params) as conn:
|
||
with conn.cursor() as cur:
|
||
# Получение всех записей result_in_stage и связанных с ними данных
|
||
cur.execute(
|
||
"""
|
||
SELECT rs.id_result_in_stage, rs.id_participant_request, pr.id_competition
|
||
FROM result_in_stage rs
|
||
JOIN participant_request pr ON rs.id_participant_request = pr.id_participant_request
|
||
"""
|
||
)
|
||
results = cur.fetchall()
|
||
|
||
# Получение id судей с учетом id_competition
|
||
cur.execute(
|
||
"""
|
||
SELECT id_judge_request, id_competition FROM judge_request
|
||
"""
|
||
)
|
||
judges = cur.fetchall()
|
||
|
||
# Сопоставление судей с соревнованиями
|
||
judge_map = {}
|
||
for judge_id, comp_id in judges:
|
||
if comp_id in judge_map:
|
||
judge_map[comp_id].append(judge_id)
|
||
else:
|
||
judge_map[comp_id] = [judge_id]
|
||
|
||
for result in results:
|
||
id_result_in_stage, id_participant_request, id_competition = result
|
||
|
||
# Выбор случайного судьи для данного соревнования
|
||
if id_competition in judge_map:
|
||
id_judge_request = random.choice(judge_map[id_competition])
|
||
else:
|
||
# Если нет судьи для соревнования, пропускаем
|
||
raise KeyError(f"Нету судей для соревнования {id_competition}")
|
||
|
||
# Выбираем случайное количество серий выстрелов
|
||
num_shot_series = random.randint(3, 10)
|
||
|
||
for _ in range(num_shot_series):
|
||
score = random.randint(2, 10)
|
||
photo = "path/to/photo.jpg" # Пример пути к фото
|
||
|
||
# Вставка данных в таблицу shot_series
|
||
cur.execute(
|
||
"""
|
||
INSERT INTO shot_series (score, photo, id_participant_request, id_result_in_stage, id_judge_request)
|
||
VALUES (%s, %s, %s, %s, %s);
|
||
""",
|
||
(
|
||
score,
|
||
photo,
|
||
id_participant_request,
|
||
id_result_in_stage,
|
||
id_judge_request,
|
||
),
|
||
)
|
||
|
||
conn.commit()
|
||
|
||
\end{lstlisting}
|
||
|
||
\end{document} |