Ladies_Man

#CDB zapiska 1

Sep 23rd, 2016
176
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Latex 125.39 KB | None | 0 0
  1. \documentclass[12pt]{article}
  2. % Эта строка — комментарий, она не будет показана в выходном файле
  3. \usepackage{ucs}
  4. \usepackage[utf8]{inputenc} % Включаем поддержку UTF8
  5. \usepackage[russian]{babel}  % Включаем пакет для поддержки русского языка
  6. \title{Отчёт по численным методам}
  7. \date{}
  8. \author{}
  9.  
  10. \usepackage{geometry} % А4, примерно 28-31 строк(а) на странице   
  11.     \geometry{paper=a4paper}
  12.    \geometry{includehead=false} % Нет верх. колонтитула
  13.     \geometry{includefoot=true}  % Есть номер страницы
  14.     \geometry{bindingoffset=0mm} % Переплет    : 0  мм
  15.     \geometry{top=20mm}          % Поле верхнее: 20 мм
  16.     \geometry{bottom=25mm}       % Поле нижнее : 25 мм
  17.     \geometry{left=25mm}         % Поле левое  : 25 мм
  18.     \geometry{right=25mm}        % Поле правое : 25 мм
  19.     \geometry{headsep=10mm}  % От края до верх. колонтитула: 10 мм
  20.     \geometry{footskip=20mm} % От края до нижн. колонтитула: 20 мм
  21. \usepackage{amsmath}    % \bar    (матрицы и проч. ...)
  22. \usepackage{amsfonts}   % \mathbb (символ для множества действительных чисел и проч. ...)
  23. \usepackage{mathtools}  % \abs, \norm
  24.     \DeclarePairedDelimiter\abs{\lvert}{\rvert}
  25.    \DeclarePairedDelimiter\norm{\lVert}{\rVert}
  26. \usepackage{listings} %листинги
  27.  
  28. \lstset{
  29.  basicstyle=\ttfamily,
  30.  columns=fullflexible,
  31.  keepspaces=true,
  32.  frame=top,frame=bottom,
  33. }
  34.  
  35. \usepackage[table,xcdraw]{xcolor}
  36.  
  37.  %для подсветки листинга javascript
  38.  \usepackage{color}
  39. \definecolor{lightgray}{rgb}{.9,.9,.9}
  40. \definecolor{darkgray}{rgb}{.4,.4,.4}
  41. \definecolor{purple}{rgb}{0.65, 0.12, 0.82}
  42.  
  43. \lstdefinelanguage{JavaScript}{
  44.  keywords={typeof, new, true, false, catch, function, return, null, catch, switch, var, if, in, while, do, else, case, break},
  45.  keywordstyle=\color{blue}\bfseries,
  46.  ndkeywords={class, export, boolean, throw, implements, import, this},
  47.  ndkeywordstyle=\color{darkgray}\bfseries,
  48.  identifierstyle=\color{black},
  49.  sensitive=false,
  50.  comment=[l]{//},
  51.  morecomment=[s]{/*}{*/},
  52.  commentstyle=\color{purple}\ttfamily,
  53.  stringstyle=\color{red}\ttfamily,
  54.  morestring=[b]',
  55.  morestring=[b]"
  56. }
  57.  
  58. \lstset{
  59.    language=JavaScript,
  60.    %backgroundcolor=\color{lightgray},
  61.    extendedchars=true,
  62.    basicstyle=\footnotesize\ttfamily,
  63.    showstringspaces=false,
  64.    showspaces=false,
  65.    %numbers=left,
  66.    %numberstyle=\footnotesize,
  67.    %numbersep=9pt,
  68.    %tabsize=2,
  69.    breaklines=true,
  70.    showtabs=false,
  71.    captionpos=b
  72. }
  73.  
  74.  
  75. \begin{document}
  76.    \newpage
  77.    {
  78.        \thispagestyle{empty}
  79.        \centering
  80.        
  81.        \textbf{
  82.        МОСКОВСКИЙ ГОСУДАРСТВЕННЫЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ ИМЕНИ Н. Э. БАУМАНА \\
  83.        Факультет информатики и систем управления \\
  84.        Кафедра теоретической информатики и компьютерных технологий}
  85.        \bigskip
  86.        \bigskip
  87.        \bigskip
  88.        \bigskip
  89.        \bigskip
  90.        \bigskip
  91.        \bigskip
  92.  
  93.        \vfill
  94.  
  95.        {\large Лабораторная работа №3}\\
  96.        по курсу <<Численные методы>>\\
  97.     \LARGE{<<Построение для таблично-заданной функции \\
  98.             кубического сплайна,\\
  99.             сплайна Акимы, \\
  100.             Б-сплайна>>\\ }
  101.     \normalsize
  102.  
  103.        \bigskip
  104.        \vfill
  105.        \hfill\parbox{5cm} {
  106.            Выполнил:\\
  107.  
  108.            Проверила:\\
  109.  
  110.        }
  111.        \vspace{\fill}
  112.  
  113.        
  114.        Москва \number\year
  115.        \clearpage
  116.    }
  117.     \newpage
  118.     {
  119.         \tableofcontents
  120.         \clearpage
  121.     }
  122.    
  123.    
  124.    
  125.    
  126.    {
  127.        \section{ВВЕДЕНИЕ}
  128.    }
  129.    
  130.     Основная цель данной работы - конвертировать базу данных автоматизированной системы тестирования T-BMSTU, используемой на кафедре ИУ9 для проведения лабортаторных работ по курсам программирования, в формат MySQL. Сравнить производительность новой реализации базы и, при необходимости, оптимизировать запрос к базе, либо оптимизировать имеющуюся базу данных в новом формате. \\
  131.    
  132.     В ходе работы будет исследована имеющаяся исходная реализация базы в формате SQLite. Затем, данные из исходной базы будут извлечены и перенесены в новую, целевую базу данных с помощью конвертера. Затем в новый формат будет конвертирован модельный SQL запрос и будет исследовано его выполнение на новой базе данных. Этот запрос будет оптимизирован под целевую реализацию базы данных. \\
  133.    
  134.     Путем сравнения производительности реализаций ``новой'' и ``старой'' баз данных и запросов будет принято решение о целесообразности перехода на новую реализацию.
  135.    
  136.    \clearpage
  137.    
  138.    
  139.    
  140.         {
  141.             \section{НЕОБХОДИМЫЕ ТЕОРЕТИЧЕСКИЕ СВЕДЕНИЯ}
  142.         }
  143.    
  144.         Прежде всего стоит рассмтореть отличительные черты исходной и целевой СУБД. После этого мы сможем заключить, возможна ли в теории выгода от прехода от одной СУБД к другой.\\
  145.    
  146.    
  147.         {
  148.             \subsection{SQLite. Преимущества и недостатки}
  149.         }
  150.    
  151.     SQLite является популярной встраиваемой реляционной базой данных. Релиз последней на данный момент версии одноименной СУБД (SQLite 3.14.1) состоялся в августе 2016 года. СУБД выпускается под общественной (public domain) лицензией, не накладывающей никаких ограничений на использование.
  152.    
  153.     Рассторим теперь особенности базы данных:\\
  154.    
  155.    
  156.     Главным отличием SQLite от других баз данных является парадигма, в которой создана база. В отличие от большинста остальных баз данных, использующих клиент-серверную архитектуру, само приложение SQLite по сути является сервером. SQLite не является отдельным процессом. Вместо этого она предоставляет библиотеку для создания `подключений` к единственному файлу, в виде которого она находится в конечной системе.
  157.    
  158.         Относительная простота реализации такого подхода является, пожалуй, главной отличительной чертой этой СУБД. Вся база хранится в одном файле. Это позволяет существенно экономить ресурсы системы, сокращает время отклика и существенно упрощает логику работы программ, использующих эту БД. База данных является единственным файлом в кросплатформенном формате, что обеспечивает бОльшую мобильность по сравнению с другими СУБД, так как для работы с БД в новой системе, развертывание базы не треубется. \\
  159.        
  160.  
  161.     Однако, у такой реализации есть и обратная сторона. Простота реализации достигается за счет того, что во время записи весь файл блокируется одним процессом. А значит, несколько процессов, одновременно подключенных к базе могут лишь считывать данные, в то время, как только один из них может изменять эти данные. Этот принцип, ``читают многие - пишет один'', является одним из недостатков этой СУБД. \\
  162.    
  163.     Еще одним недостатком является отсутствие полной поддержки SQL-92:
  164.     \begin{itemize}
  165.         \item[] Не поддерживается, например, удаление или изменение столбца в таблице:
  166.             \begin{itemize}
  167.             \item[] \verb| ALTER TABLE DROP COLUMN ... |
  168.             \item[] \verb| ALTER TABLE ALTER COLUMN ... | отсутствуют в SQLite
  169.             \end{itemize}
  170.         \item[] Опущены \verb| RIGHT OUTER JOIN | и \verb| FOR EACH STATEMENT |
  171.         \item[] По умолчанию отключена поддержка foreign key
  172.         \item[] Недоступны хранимые процедуры
  173.         \item[] Триггеры SQLite намного менее функциональны, нежели триггеры других СУБД
  174.     \end{itemize}
  175.        
  176.     Для взаимодйствия с SQLite из приложений отсутствуют официальные драйвера. Таковых нет ни под JDBC, ни под ADO.Net, ни под ODBC. Отстутствие этого ``из коробки'' яляется существенным минусом SQLite.\\
  177.    
  178.        
  179.     Еще одной особенностью SQLite является ``слабая типизация'', или концепция ``близости типов'' (type affinity). Так, тип столбца не определяет тип хранимого в этом столбце значения. В любой столбец может быть записано любое значение, а сам тип столбца используется для приведения значений к одному типу при сравнении значений. Все это позволяет создавать таблицу ``простым''  \verb| CREATE TABLE sampleTable| \verb|(col1, col2, col3) | без указания чего либо еще, что является недопустимым и недоступным в других СУБД.
  180.    
  181.     Однако, в SQLite доступны лишь 5 типов данных:
  182.     \begin{itemize}
  183.         \item[] NULL
  184.         \item[] INTEGER (знаковое целое число до 8 байт)
  185.         \item[] REAL (число с плавающей точкой, 8 байт в формате IEEE)
  186.         \item[] TEXT (строка в кодировке UTF-8 или UTF-16)
  187.         \item[] BLOB (входное значение, `как есть`)
  188.     \end{itemize}
  189.        
  190.         Согласно руководству, для хранения типа Boolean рекомендуется использовать INTEGER 0 или 1, а Date и Time типы хранить в виде строк. Вышесказанное является одновременно как недостатком, так и достоинством и не может быть однозначно интерпретировано в рамках `общих` задачах. \\
  191.        
  192.        
  193.     В SQLite также отсутствуют какие-либо механизмы репликации.
  194.    
  195.     Отсутствует система пользователей.
  196.    
  197.     Отсутствует возможность увеличения производительности.\\
  198.    
  199.     Несмторя на все это, SQLite является отличным кандидатом на использование в качестве встраиваемой системы и пользуется большой популярностью в этой сфере.
  200.        
  201.        
  202.         {
  203.             \subsection{MySQL. Преимущества и недостатки}
  204.         }
  205.        
  206.         Теперь взглянем на целевую базу данных, MySQL.
  207.        
  208.         MySQL является самой распространенной СУБД. Разрабатывается корпорацией Oracle, как доступная под универсальной общественной лицензией GNU (GNU General Public License) замена промышленной БД Oracle.
  209.        
  210.         Рассмотрим особенности MySQL:\\
  211.        
  212.        
  213.         MySQL разработана в соответствии с ``классической'' клиент-серверной архитектурой и поддерживает все основные ОС. Для работы с базой из приложений имеются официальные драйвера ADO.Net, JDBC и ODBC. Хотя количество языков, поддерживаемых API MySQL и меньше, чем у SQLite, недостатком это не является, так как упущены не самые популярные в настоящее время языки, такие, как Basic, Forth и Fortran.\\
  214.        
  215.    
  216.         MySQL поддерживает большое количество типов данных:
  217.             \begin{itemize}
  218.             \item[] TINYINT (BOOL), SMALLINT, MEDIUMINT, INTEGER, BIGINT для целочисленных значений
  219.             \item[] FLOAT, DOUBLE, NUMERIC, REAL для значений с плавающей точкой
  220.             \item[] DATE, TIME, DATETIME, YEAR, TIMESTAMP для значений даты и времени
  221.             \item[] CHAR, VARCHAR для строковых значений фиксированной / переменной длины
  222.         \item[] TINYTEXT, TEXT, MEDIUMTEXT, LONGTEXT для тектовых значений длины $2^{8}-1$ / $2^{16}-1$ / $2^{24}-1$ / $2^{32}-1$ соответственно
  223.             \item[] TINYBLOB, BLOB, MEDIUMBLOB, LONGBLOB для значений `как есть`
  224.             \item[] ENUM, SET для занчений типа перечисление / множество
  225.             \end{itemize}
  226.            
  227.     Это является существенным преимуществом MySQL перед SQLite, так как таблицы могут быть построены более оптимально в соответствии с бизнес логикой.\\
  228.    
  229.    
  230.     Поддерживается почти полный стандарт SQL-92 (DML, DDL, DCL), хотя и присутствует проприетарное расширение синтаксиса. В частности, в этой БД используется свой синтаксис для написания триггеров, а также хранимых процедур, что не поддерживаются в SQLite. Однако, стоит отметить, что в MySQL упущены, в частности, INSTEAD OF триггеры, хотя они и могут быть самостоятельно реализованы отдельно.\\
  231.    
  232.    
  233.     В Mysql присутствуют механизмы репликации, так как разработчики создавали функциональность по заказу лицензионных пользователей и это (репликация) является важным фактором при выборе БД для коммерческого использования. Так, поддерживается
  234.         \begin{itemize}
  235.             \item[] Многомастерная (Multi-master replication) репликация. Данные в данном случае хранятся группой устройств и могут быть изменены любым устройством из этой группы `мастеров`, так и
  236.             \item[] Система с ведущими и ведомыми устройствами (Master-slave), где ведущая база данных рассматривается, как `авторитетный` источник данных, а подчиненные синхронизируются с ней.
  237.         \end{itemize}
  238.        
  239.     Все это существенно повышает надежность работы этой БД и отличает ее от SQLite, где механизмы репликации впринципе отсутствуют.\\
  240.    
  241.    
  242.     Mysql поддерживает многопоточную работу с помощью механизма блокировки таблиц или строк, что является более вариативной функциональностью, нежели захват всего файла базой SQLite.
  243.    
  244.     Присутствуют мехнизмы управления уровнем доступа пользователей, хотя отсутствуют механизмы деления пользователей на роли и группы.
  245.    
  246.     Система Mysql масштабируема.
  247.    
  248.     Также, среди преимуществ можно отметить наличие множества ``движков'' (database engine), каждый из которых обладает своими преимуществами и может быть выбран согласно решаемой задаче. В ходе работы некоторые из движков будут рассмторены чуть подробнее.
  249.    
  250.     Благодаря свободному доступу к исходному коду, эта СУБД может быть подстроена под индивидуальное решение. А вывсокая популярность системы обеспечивает наличие поддержки по многим возможным проблемам.\\
  251.    
  252.    
  253.     Исходя из всего вышеперечисленного, переход с SQLite на MySQL видится разумным в рамках большей части задач общего назначения. MySQL объективно обладает большим числом преимуществ перед SQLite.
  254.    
  255.     Однако, в рамках курсовой работы рассматривается конкретная база данных автоматизированной системы тестирования T-BMSTU. И для оценки реальной выгоды от перехода с одной базы на другую необходимо прежде всего оценить уместность преимуществ MySQL перед SQLite в рамках конкретной задачи.
  256.    
  257.     Для этого рассмторим теперь исходную базу данных T-BMSTU.
  258.    
  259.     \bigskip
  260.    
  261.    
  262.     {
  263.         \section{ИЗУЧЕНИЕ ВХОДНЫХ ДАННЫХ}
  264.     }
  265.    
  266.     Исходными данными курсовой работы являются SQL скрипт создания базы данных, непосредственно база данных в виде файла {\it tbmstu.db} и запрос, формирующий выборку данных из базы для отображения на Web-сервере тестирования.\\
  267.    
  268.     {
  269.         \subsection{Исходная база данных T-BMSTU}
  270.     }
  271.     Взглянем на схему базы данных
  272.    
  273.     <здесь будет схема базы>
  274.     %тут картинка схемы большая будет и вертикальная мб даже
  275.    
  276.     Исходная база данных содержит в себе 22 таблицы:
  277.     \begin{verbatim}
  278.        Institutions
  279.        Subjects
  280.        Modules
  281.        Groups
  282.        RelGroupsModules
  283.        Persons
  284.        Sessions
  285.        CurrentAdmins
  286.        CurrentTaskAuthors
  287.        Students
  288.        ModuleAuthors
  289.        Approvers
  290.        Languages
  291.        Tasks
  292.        RelTasksLanguages
  293.        RelLanguagesModules
  294.        Submissions
  295.        FailedTests
  296.        Comments
  297.        Approvements
  298.        RelTasksModules
  299.     \end{verbatim}
  300.        
  301.         %отсортировать в порядке создания как по скрипту
  302.  
  303.     Также имеются 6 представлений:
  304.     \begin{verbatim}
  305.        TimeDesc
  306.        RelModulesInstitutions
  307.        RelSubmissionsApprovers
  308.        FinalApprovements
  309.        PersonRoles
  310.        RelStudentsTasks
  311.     \end{verbatim}
  312.    
  313.     Присутствуют 7 триггеров, выводящих сообщение об ошибке, в случае нарушений работы с внешники ключами (вставки и обновления):
  314.     \begin{verbatim}
  315.        fki_RelGroupsModules
  316.        fku_RelGroupsModules
  317.        fki_Tasks_PersonId
  318.        fku_Tasks_PersonId
  319.        fki_Approvements_PersonId
  320.        fku_Approvements_PersonId
  321.        fku_Submissions_PassedTests
  322.     \end{verbatim}
  323.    
  324.     Назначение таблиц, представлений и триггеров понятно из их названий.
  325.    
  326.     В большинстве таблиц в базе хранится по несколько десятков записей - это таблицы со списками студентов групп, предметов и модулей, таблица-временная шкала, таблица авторов заданий. Есть таблицы на одну или несколько сотен записей: аккаунты в системе, задания, проверяющие и вспомогательные таблицы. Есть таблица университетов с одной записью, так как на данный момент система применятеся только в МГТУ. И есть 4 таблицы с десятками тысяч записей. В порядке убывания по количеству записей (по данным на февраль 2016):
  327.    
  328.     \begin{itemize}
  329.         \item[] Sessions, ~ 130 тыс. записей
  330.         \item[] Submissions, ~ 80 тыс. записей
  331.         \item[] Comments, ~ 70 тыс. записей
  332.         \item[] Approvements, ~70 тыс. записей.
  333.     \end{itemize}
  334.    
  335.     Очевидно, что работа с этими таблицами представляет наибольшую сложность не только по причине частой записи в них, но и по причине того, что эти таблицы имеют дочерние записи и ссылаются на большое число других таблиц. Рассмотрим, например таблицу (скрипт создания) решений, отправленных на сервер тестирования - таблицу {\it Submissions}:\\
  336.        
  337. \begin{lstlisting}[caption={Скрипт создания таблицы Submissions}, label={lst:table_submissions}]
  338. CREATE TABLE Submissions (
  339.    SubmissionID    INTEGER NOT NULL PRIMARY KEY,
  340.  
  341.    PersonID    INTEGER NOT NULL,
  342.    GroupID     INTEGER NOT NULL,
  343.    TaskID      INTEGER NOT NULL,
  344.    ModuleID    INTEGER NOT NULL,
  345.    SubmissionTime  TEXT NOT NULL CHECK (length(SubmissionTime) < 32),
  346.  
  347.    LanguageID  INTEGER NOT NULL,
  348.    SourceCode  TEXT NOT NULL CHECK (length(SourceCode) < 50*1024),
  349.    Draft       INTEGER CHECK (Draft = 0 OR Draft = 1),
  350.  
  351.    SentToTestTime  TEXT,
  352.    PassedTests INTEGER,
  353.    TestingServerId INTEGER,
  354.  
  355.    UNIQUE (PersonID, GroupID, TaskID, ModuleID, SubmissionTime),
  356.    FOREIGN KEY (PersonID, GroupID) REFERENCES Students
  357.                                    ON DELETE CASCADE ON UPDATE CASCADE,
  358.    FOREIGN KEY (TaskID, ModuleID)  REFERENCES RelTasksModules
  359.                                    ON DELETE RESTRICT ON UPDATE CASCADE,
  360.    FOREIGN KEY (GroupID, ModuleID) REFERENCES RelGroupsModules
  361.                                    ON DELETE RESTRICT ON UPDATE CASCADE,
  362.    FOREIGN KEY (TaskID, LanguageID) REFERENCES RelTasksLanguages
  363.                                    ON DELETE RESTRICT ON UPDATE CASCADE,
  364.    FOREIGN KEY (LanguageID, ModuleID) REFERENCES RelLanguagesModules
  365.                                    ON DELETE RESTRICT ON UPDATE CASCADE
  366. );
  367.         \end{lstlisting}
  368.        
  369.  
  370.     Как видно из листинга ~\ref{lst:table_submissions}, эта таблица ссылается сразу на 5 других таблиц. А значит, при добавлении записи в нее необходимо обратиться сразу к 5 таблицам. Также с этой таблицей работает триггер {\it fku\_Submissions\_PassedTests}, проверяющий наличие валидного сервера тестирования, назначенного по решению.
  371.    
  372.     Тут же становятся видны ограничения, озвученные ранее при анализе SQLite ``из коробки'', такие, как ограниченное число типов данных: {\it SubmissionTime} хранится в виде строки TEXT, {\it Dratf} хранится в виде INTEGER'а. Из-за этого приходится осуществлять валидацию входных данных проверяя длину или значние.
  373.    
  374.     Также необходимо соблюдение уникальности группы полей {\it PersonID, GroupID, TaskID, ModuleID, SubmissionTime} и PRIMARY KEY.
  375.    
  376.     Работа с этой таблицей весьма труднозатратна относительно работы с остальными таблицами в рамках базы данных.
  377.    
  378.     У этой таблицы имеются сразу 4 вручную созданных индекса. Учтем это в дальнейшем при возможной оптимизации этой таблицы. \\
  379.    
  380.     Таблиц, подобных этой, в базе еще 3. Нужно отметить, что таблица {\it Sessions} выделяется среди ``крупных таблиц'' своими размерами, а также относительной простотой работы: в ней есть только валидация ip-адреса по длине и ссылка на таблицу пользователей сервера для прикрепления его к сеансу. Поиск по этой таблице не нужен, поэтому есть только стандартный PRIMARY KEY
  381.    
  382.     Эти 4 таблицы постоянно используются в ходе работы сервера. Данные в них обновляются постоянно.
  383.    
  384.     Большинство данных остальных таблиц редактируется либо с началом модуля (например, таблицы {\it Modules, Tasks}), либо семестра (такие, как {\it Subjects, Groups, TimeScale}), либо с началом учебного года ({\it Persons, Groups, TimeScale}). Таблицы {\it Languages, CurrentAdmins, CurrentTaskAuthors, Institutions} редактируются еще реже. То есть, данные большей части таблиц редактируются совсем не часто. Но, бОльшая часть данных всей базы редактируется постоянно. Учтем это в дальнейшем.\\
  385.    
  386.     {
  387.         \subsection{Исходный запрос к базе}
  388.     }
  389.    
  390.     Во входных данных также присутствует запрос к описанной выше базе. Этот запрос формирует выборку данных для отображения на странце Web-сервера.
  391.     Взглянем на запрос в его первоначальном варианте:\\
  392.        
  393.         %шрифт листинга поменьше бы чтоб хотя ы на 2 страницы
  394.        
  395.         \begin{lstlisting}[caption={Скрипт выборки из базы}, label={lst:bigq}]
  396. SELECT DISTINCT
  397.    Institutions.InstitutionId, Institutions.InstitutionName,
  398.        /*****/
  399.    Groups.GroupId, Groups.TimeId, TimeDesc.TimeName, Groups.GroupName,
  400.        /*****/
  401.    Subjects.SubjectId, Subjects.SubjectName,
  402.        /*****/
  403.    Modules.ModuleId, Modules.ModuleNo, Modules.ModuleName, Modules.MinRating,
  404.    RelGroupsModules.ExpireTime, IsStudent, IsApprover,
  405.        /*****/
  406.    StTaskId, StTaskNo, StTaskName, StTaskRating, Status,
  407.        /*****/
  408.    SubmissionId, SubmPersonId, SubmLogin, SubmFirstName,
  409.    SubmLastName, SubmTaskId, SubmTaskName, SubmissionTime
  410. FROM (
  411.    SELECT GroupId, ModuleId, max(IsStudent) AS IsStudent,
  412.                              max(IsApprover) AS IsApprover
  413.    FROM (
  414.        SELECT Students.GroupId, ModuleId, 1 AS IsStudent, 0 AS IsApprover
  415.        FROM Students
  416.        JOIN RelGroupsModules USING(GroupId)
  417.        WHERE PersonId = 1
  418.        UNION SELECT GroupId, ModuleId, 0 AS IsStudent, 1 AS IsApprover
  419.        FROM Approvers
  420.        WHERE PersonId = 1
  421.        )
  422.    GROUP BY GroupId, ModuleId
  423.    )
  424. JOIN RelGroupsModules USING(GroupId, ModuleId)
  425. JOIN Groups USING(GroupId)
  426. JOIN Institutions USING(InstitutionId)
  427. JOIN TimeDesc USING(TimeId)
  428. JOIN Modules USING(ModuleId)
  429. JOIN Subjects USING(SubjectId)
  430. LEFT OUTER JOIN (
  431.    SELECT * FROM (
  432.        SELECT DISTINCT GroupId, ModuleId,
  433.            TaskId AS StTaskId, TaskNo AS StTaskNo, TaskName AS StTaskName,
  434.            TaskRating AS StTaskRating,
  435.            (CASE
  436.                WHEN count(SubmissionId) = 0 THEN 0
  437.                WHEN max(AcceptedInTime) = 1 THEN 3
  438.                WHEN max(AcceptedOutdated) = 1 THEN 4
  439.                WHEN count(SubmissionId) > count(AcceptedInTime)+
  440.                                           count(AcceptedOutdated) THEN 1
  441.                ELSE 2
  442.            END) AS Status,
  443.            NULL AS SubmissionId, NULL AS SubmPersonId,
  444.            NULL AS SubmLogin, NULL AS SubmFirstName,
  445.            NULL AS SubmLastName, NULL AS SubmTaskId,
  446.            NULL AS SubmTaskName, NULL AS SubmissionTime
  447.        FROM (
  448.            SELECT DISTINCT Students.PersonId,
  449.                RelGroupsModules.GroupId, RelGroupsModules.ModuleId,
  450.                RelTasksModules.TaskId, RelTasksModules.TaskNo, Tasks.TaskName,
  451.                RelTasksModules.TaskRating, Submissions.SubmissionId,
  452.                (SELECT Accepted
  453.                FROM Approvements
  454.                WHERE Approvements.SubmissionId = Submissions.SubmissionId
  455.                    AND (RelGroupsModules.ExpireTime IS NULL
  456.                    OR Submissions.SubmissionTime < RelGroupsModules.ExpireTime)
  457.                ORDER BY ApprovementTime DESC
  458.                LIMIT 1
  459.                ) AS AcceptedInTime,
  460.                (SELECT Accepted
  461.                FROM Approvements
  462.                WHERE Approvements.SubmissionId = Submissions.SubmissionId
  463.                    AND RelGroupsModules.ExpireTime IS NOT NULL
  464.                    AND Submissions.SubmissionTime >= RelGroupsModules.ExpireTime
  465.                ORDER BY ApprovementTime DESC
  466.                LIMIT 1
  467.                ) AS AcceptedOutdated
  468.            FROM Students, RelGroupsModules
  469.            LEFT OUTER JOIN RelTasksModules USING(ModuleId)
  470.            LEFT OUTER JOIN Tasks USING(TaskId)
  471.            LEFT OUTER JOIN Submissions
  472.                ON Submissions.PersonId = 1
  473.                AND Submissions.GroupId = RelGroupsModules.GroupId
  474.                AND Submissions.TaskId = RelTasksModules.TaskId
  475.                AND Submissions.ModuleId = RelGroupsModules.ModuleId
  476.                AND (Submissions.Draft IS NULL OR Submissions.Draft = 0)
  477.            WHERE Students.PersonId = 1 AND
  478.                Students.GroupId = RelGroupsModules.GroupId
  479.            )
  480.        GROUP BY GroupId, ModuleId, TaskId
  481.        )
  482.    UNION SELECT DISTINCT Approvers.GroupId, Approvers.ModuleId,
  483.        NULL AS StTaskId, NULL AS StTaskNo, NULL AS StTaskName,
  484.        NULL AS StTaskRating, 0 AS Status,
  485.        Submissions.SubmissionId, Submissions.PersonId AS SubmPersonId,
  486.        Persons.Login AS SubmLogin, Persons.FirstName AS SubmFirstName,
  487.        Persons.LastName AS SubmLastName, Tasks.TaskId AS SubmTaskId,
  488.        Tasks.TaskName AS SubmTaskName, Submissions.SubmissionTime
  489.    FROM Approvers
  490.    LEFT OUTER JOIN Submissions
  491.        ON Submissions.GroupId = Approvers.GroupId
  492.        AND Submissions.ModuleId = Approvers.ModuleId
  493.        AND Draft = 0
  494.        AND NOT EXISTS (
  495.            SELECT * FROM Approvements
  496.            WHERE Approvements.SubmissionId = Submissions.SubmissionId
  497.        )
  498.    LEFT OUTER JOIN Tasks
  499.        ON Tasks.TaskId = Submissions.TaskId
  500.    LEFT OUTER JOIN Persons
  501.        ON Persons.PersonId = Submissions.PersonId
  502.    WHERE Approvers.PersonId = 1
  503.    ) AS Cont
  504.    ON Cont.GroupId = Groups.GroupId
  505.    AND Cont.ModuleId = Modules.ModuleId
  506. ORDER BY
  507.    Institutions.InstitutionId ASC,
  508.    Groups.TimeId DESC,
  509.    Groups.GroupId ASC,
  510.    SubjectId ASC,
  511.    ModuleNo ASC,
  512.    StTaskNo ASC,
  513.    SubmissionTime ASC;
  514.         \end{lstlisting}
  515.        
  516.         В запросе из Листинга ~\ref{lst:bigq} присутствуют 11 выборок с помощью SELECT из большей части таблиц базы. В том числе есть обращения к ``большим'' таблицам - {\it Submissions} и {\it Approvements}. Присутствует множество объединений таблиц, несколько группировок по учебным модулям и группам а также с использованием агрегирующих функций, несколько DISTINCT выборок и сортировок.
  517.        
  518.         Выполнять оптимизации согласно заданию, будем ``с оглядкой'' на этот запрос.\\
  519.        
  520.        
  521.         \clearpage
  522.        
  523.         {
  524.                 \section{ПОРТИРОВАНИЕ БАЗЫ ДАННЫХ}
  525.             }
  526.        
  527.         Теперь, когда мы получили представление об устройсве исходной базы данных SQLite, потрируем ее на целевую платформу MySQL.
  528.        
  529.         Стандартные реверс-инжиниринговые утилиты для портирования могут некорректно взаимодействовать со схемой базы данных и в результате некоторые таблицы могут не создаться, а внутреннее устройство других будет отличаться. К тому же нам необходимо произвести некоторые изменения типов согласно best practices, описанных в руководстве пользователя MySQL. %здесь ссыль
  530.        
  531.         В случае данной курсовой работы, у нас имеется преимущество перед такими утилитами в виде еще одного входного скрипта - скрипта создания базы. Все что необходимо сделать в данном случае - перенести непосредственно данные в подготовленную на новом месте схему.\\
  532.        
  533.        
  534.         Так как обе СУБД не полностью поддерживают формат SQL-92, простым запуском скрипта создания базы обойтись не удастся. Преобразуем исходный скрипт с учетом отличий диалекта SQL, используемого в SQLite от диалекта, используемого в MySQL. Также выполним преобразования типов данных, заменив строки, содержащие даты и время на тип DATETIME. Другие строковые константы заменим на тип VARCHAR, более предпочтительный в рамках MySQL. Своеобразный Boolean в SQLite, реализованый через INTEGER и проверку значений с помощью CHECK() заменим на TINYINT, являющийся, аналогом BOOLEAN'а в MySQL. Проведем еще некоторые преобразования и портируем триггеры, переведя их на проприетарный синткасис триггеров и хранимых прцедур MySQL.\\
  535.        
  536.        
  537.         Считаем, что схема базы данных уже подготовлена. Напишем небольшую утилиту на $C\#$, которая с помощью технологии ADO.Net доступа приложений на платформе .Net к данным выберет все данные из базы SQLite и вставит в подготовленную схему базы MySQL. Единственной возможной проблемой является отсутствие официальных драйверов SQLite для ADO.Net, однако это решается загрузкой аналога  через встроенный менеджер пакетов NuGet. Запускаем утилиту и получаем на выходе наполненную базу данных MySQL с описанными выше изменениями. Триггеры необходимо добавить вручную.
  538.        
  539.         Поскольку никаких других существенных изменений сделано не было, схема базы данных и внутреннее устройство таблиц с точностью до типов некоторых столбцов остались без изменений и повторно приводить их не имеет смысла. Портирование на этом завершено. Считаем, что имеется полный MySQL аналог исходной SQLite базы.\\
  540.        
  541.        
  542.         {
  543.                 \section{MYSQL ОПТИМИЗАЦИИ}
  544.             }
  545.            
  546.             На данный момент имеется база данных MySQL. Можно считать, что все дальнейшие операции, если не оговорено иное, происходят над ней.
  547.            
  548.             Запрос, рассмотренный ранее в Листинге ~\ref{lst:bigq} не сработает в своем исходном виде на новой базе из-за отличия используемых диалектов. В MySQL у каждой выборки должен быть свой алиас (alias, псевдоним). Проименуем все таблицы в запросе, добавив к ним соответствующие технические наименования.
  549.            
  550.             Теперь мы можем выполнить запрос на новой базе. Выполняем и фиксируем результат запроса, чтобы при дальнейшем его изменении их можно было сравнить и проверить, выдают они одинаковую выборку или нет.\\
  551.            
  552.             Перейдем теперь к главной части этой работы - исследованию и оптимизации запроса и/или базы.\\
  553.            
  554.                        
  555.             {
  556.                 \subsection{Исследование выполнения запроса на базе MySQL}
  557.             }
  558.            
  559.             Как уже отмечалось ранее, у MySQL имеются некоторые преимущества перед SQLite. Среди них - наличие команды EXPLAIN. При выполненеии любого запроса, оптимизатор запросов MySQL создает наиболее оптимальный план его выполнения. Этот план можно посмтореть с помощью команды EXPLAIN. Эта команда является одним из самых мощных инструментов разработчика, доступных в MySQL. \\
  560.            
  561.             Выполним эту команду вместе с запросом из Листинга ~\ref{lst:bigq}:
  562.             %здесь результат
  563.            
  564.             <здесь результат первого EXPLAINа>
  565.             %ссылка на рез-т эксплэина
  566.            
  567.             Взглянем на результат выполнения запроса в таблице <ссылка на рез-т explain'а>.
  568.             Результат выполнения состоит из 11 столбцов:
  569.             \begin{itemize}
  570.                 \item[] Id - порядковый номер SELECT'а внутри запроса
  571.                 \item[] Select\_type - тип запроса SELECT. Среди возможных значений:
  572.                 \begin{itemize}
  573.                     \item[] PRIMARY - самый внешний запрос в JOIN'е
  574.                     \item[] DERIVED - данный запрос является подзапросом
  575.                     \item[] SUBQUERY - первый SELECT в подзапросе
  576.                     \item[] UNION - второй или последующий SELECT в UNION'е
  577.                     \item[] UNION RESULT - результат UNION'а
  578.             \end{itemize}
  579.             \item[] Table - таблица, к которой относится строка результата
  580.             \item[] Type - тип связыания таблиц. Среди возможных значений:
  581.             \begin{itemize}
  582.                     \item[] Const - таблица имеет только одну соответствующую строку, которая проиндексирована. Таблица в данном случае читается лишь раз и в дальнейшем значение строки воспринимается, как константа. Это наиболее быстрый тип свзяывания
  583.                     \item[] Eq\_ref - все части PRIMARY KEY или UNIQUE NOT NULL индекса используются для связывания. Еще один наилучший тип связывания
  584.                     \item[] Ref - прямая ссылка. Все строки индексного столбца противопоставляются строкам предыдущей таблицы. Неплохой вариант
  585.                     \item[] Index - сканирование всего индексного дерева для поиска строк
  586.                     \item[] All - худший тип связи. Для нахождения строк используется полнотекстовое сканирование таблицы. Указывает на отсутствие подходящих индексов в таблице
  587.             \end{itemize}          
  588.             \item[] Possible\_keys - возможные индексы. Возможно, они не используются. Значение NULL указывает на отсутствие подходящих индексов в таблице.
  589.             \item[] Key - фактически использованный ключ. Может отличаться от указанных в {\it Possible\_keys} значений
  590.             \item[] Key\_len - длина используемого ключа
  591.             \item[] Ref - столбцы или константы, которые сравниваются с индексом
  592.             \item[] Rows - число обработанных записей
  593.             \item[] Filtered - процент отфильтрованных записей
  594.             \item[] Extra - дополнительная информация об обработке запроса. Среди возможных значений:
  595.             \begin{itemize}
  596.                     \item[] Using index - информация получена с применением индексного дерева без доп. поиска для чтения строки. Возможно при всех проиндексированных столбцах.
  597.                     \item[] Using temporary - создане временной таблицы. Например, при ORDER BY на наборе стобцов, отличном от набора в GROUP BY
  598.                     \item[] Using filesort - Дополнительный проход с сохранением ключей строк, которые попали под условие WHERE и последующей сортировкой самих ключей.
  599.                     \item[] Using join buffer (Block Nested Loop) - использование буфера для сохранения таблиц с последующим их объединением путем выборки подходящих строк из буфера
  600.             \end{itemize}
  601.         \end{itemize}
  602.         Выше представлена интерпретация зачений резульатат EXPLAIN'а. Оценим результат нашего запроса:
  603.         \begin{itemize}
  604.             \item[] В процессе выполнения запроса полнотекстово сканируются сразу 6 таблиц, которые вполедствие хранятся в виде временных таблиц ({\it type: all})
  605.             \item[] В первой выборке присутствует полное сканирование индексного дерева первой просматриваемой таблицы ({\it type: index}), несмотря на то, что в таблице содержится одна запись
  606.             \item[] Большая длина используемого ключа таблицы первой выборки
  607.             \item[] Использование индексного дерева ({\it extra: using index}) для создания временной таблицы ({\it extra: using temporary}) и дополнительный проход по временной таблице для сортировки ({\it extra: using filesort}) при том, что в таблице 1 запись
  608.             \item[] Большое количество вложенных ({\it Select\_type: DERIVED}) запросов, 10 подзапросов
  609.             \item[] Присутствуют 2 таблицы ({\it id: 5,6}) с большим количеством просмотренных записей (относительно результата запроса) ({\it rows: 2247})
  610.             \item[] Отсутствуют данные о ключах в шести запросах ({\it id: 1, 4, 5, null, 2, null})
  611.             \item[] Отсутствуют данные о количестве/проценте обработанных строк ({\it rows/filtered: null}) в процессе объединения ({\it select\_type: union reslut}) выборок {\it id:5 + id:10} и {\it id:3 + id:4}
  612.         \end{itemize}
  613.        
  614.        
  615.        
  616.         {
  617.                 \subsection{Оптимизация запроса}
  618.             }
  619.        
  620.         ``Узкие места'' запроса на данный момент ясны. Присупим к оптимизации запроса. Для этого вновь обратимся к best practices по оптимизации запросов из  руководства к MySQL и выполним некоторые преобразования.\\
  621.        
  622.         {
  623.                 \subsubsection{Оптимизация условия WHERE}
  624.             }  
  625.    
  626.         Оптимизтор MySQL умеет удалять ненужные скобки, сворачивать константы, удалять ненужные условия. Однако, эти действия выполняются практически без затрат и выполнение этих преобразований вручную можно опустить, чтобы оставить запрос в более понятном и удобном для чтения виде.
  627.        
  628.         В некотрых случаях, если все столбцы в индексе числовые, MySQL может читать строки из индекса, совсем не обращаясь непосредственно к данным.\\
  629.        
  630.         Среди возможных доступных, но еще не примененных оптимизаций выделяется ``углубление'' условия WHERE в запросе. Так, при JOIN'е выборка строк, подходящих под условие, будет осуществляться до объединения, а значит при выполнении запроса не придется просматривать конечную ``большую'' выборку.
  631.         Протестируем для начала возможность подобной оптимизации:\\
  632. \begin{lstlisting}[caption={тестовый запрос до оптимизации WHERE}, label={lst:test1where_before}]
  633. SELECT SQL_NO_CACHE p.personID,  m.moduleID, s.taskID
  634.     FROM submissions s
  635.     JOIN persons p ON s.PersonID = p.PersonID
  636.     JOIN modules m ON s.ModuleID = m.ModuleID
  637.     WHERE p.PersonID BETWEEN 92 AND 109 AND m.ModuleName LIKE `%C%';
  638. \end{lstlisting}
  639.  
  640.  
  641. \begin{lstlisting}[caption={тестовый запрос после оптимизации WHERE}, label={lst:test1where_after}]
  642. SELECT SQL_NO_CACHE p.personID, m.moduleID, s.taskID
  643.     FROM submissions s
  644.     JOIN (SELECT personID FROM persons
  645.         WHERE PersonID BETWEEN 92 AND 109) p
  646.     ON p.PersonID = s.PersonID
  647.     JOIN (SELECT moduleID FROM modules WHERE ModuleName LIKE '%C%') m
  648.     ON m.ModuleID = s.ModuleID;
  649. \end{lstlisting}
  650.  
  651. \begin{table}[h]
  652.    \caption {Сравнение таймингов запросов}
  653.    \label {test1where}
  654.    \begin{tabular}{|l|c|c|}
  655.    \hline
  656.    Запрос                  & Тайминг клиентской стороны & Тайминг серверной стророны                                                                                                                                                                                       \\ \hline
  657.    До оптимизации     & 0.0471                     & 0.0463                                                                                                                                                                                                           \\ \hline
  658.    После оптимизации  & 0.0313                     & 0.0317                                                                                                                                                                                                           \\ \hline
  659.    \end{tabular}
  660. \end{table}
  661.  
  662. %\begin{table}[h]
  663. %    \caption {test1where}
  664. %    \begin{tabular}{|l|}
  665. %    \hline
  666. %    Test query before                                                                                                                                                                                                                                                                                                                                      \\ \hline
  667. %    \verb|SELECT SQL_NO_CACHE p.personID,  m.moduleID, s.taskID|\\ \verb|   FROM submissions s|\\ \verb|   JOIN persons p ON s.PersonID = p.PersonID|\\ \verb|   JOIN modules m ON s.ModuleID = m.ModuleID|\\ \verb|   WHERE p.PersonID BETWEEN 92 AND 109|\\ \verb|       AND m.ModuleName LIKE '%Программирование%';|                                                                          \\ \hline
  668. %    Client side timing: 0.0471\\Server side timing: 0.0463                                                                                                                                                                                                                                                                                                 \\ \hline
  669. %    Test query after                                                                                                                                                                                                                                                                                                                                       \\ \hline
  670. %    \verb|SELECT SQL_NO_CACHE p.personID, m.moduleID, s.taskID|\\ \verb|    FROM submissions s|\\ \verb|    JOIN (SELECT personID FROM persons|\\ \verb|           WHERE PersonID BETWEEN 92 AND 109) p|\\ \verb|       ON p.PersonID = s.PersonID|\\ \verb|    JOIN (SELECT moduleID FROM modules|\\ \verb|           WHERE ModuleName LIKE `%Программирование%') m|\\ \verb|       ON m.ModuleID = s.ModuleID;|\\ \hline
  671. %    Client side timing: 0.0313\\Server side timing: 0.0317                                                                                                                                                                                                                                                                                                 \\ \hline
  672. %    \end{tabular}
  673. %\end{table}
  674.  
  675.         Как видно, оптимизация при переносе условия вглубь запроса имеет место быть. Теперь применим эту оптимизацию к основному запросу:\\
  676.        
  677. \begin{lstlisting}[caption={основной запрос до I оптимизации WHERE}, label={lst:bigq_where1_before}]
  678. FROM Students as a3, RelGroupsModules as a4
  679. LEFT OUTER JOIN RelTasksModules USING(ModuleId)
  680. LEFT OUTER JOIN Tasks USING(TaskId)
  681. LEFT OUTER JOIN Submissions
  682.     ON Submissions.PersonId = 1 AND ...
  683.     AND (Submissions.Draft IS NULL OR Submissions.Draft = 0)
  684. WHERE a3.PersonId = 1
  685.     AND a3.GroupId = a4.GroupId
  686. \end{lstlisting}
  687.  
  688.  
  689. \begin{lstlisting}[caption={основной запрос после I оптимизации WHERE}, label={lst:bigq_where1_after}]
  690. FROM (
  691.     SELECT PersonID, a3.GroupID, ModuleID, ExpireTime
  692.         FROM Students as a3,
  693.         relgroupsmodules as a4
  694.         WHERE a3.PersonId = 1
  695.             AND a3.GroupID = a4.GroupId
  696. ) as pg
  697. \end{lstlisting}
  698.        
  699. %\begin{table}[h]
  700. %    \caption {main1where1}
  701. %    \begin{tabular}{|l|}
  702. %    \hline
  703. %    Main query before                                                                                                                                                                                                                                                                                                                                                                                                                                                                         \\ \hline
  704. %               FROM Students as a3, RelGroupsModules as a4\\           LEFT OUTER JOIN RelTasksModules USING(ModuleId) \\          LEFT OUTER JOIN Tasks USING(TaskId) \\          LEFT OUTER JOIN Submissions \\              ON Submissions.PersonId = 1 \\              AND Submissions.GroupId = a4.GroupId \\             AND Submissions.TaskId = RelTasksModules.TaskId \\              AND Submissions.ModuleId = a4.ModuleId \\               AND (Submissions.Draft IS NULL OR Submissions.Draft = 0) \\         WHERE a3.PersonId = 1 AND \\                a3.GroupId = a4.GroupId  \\ \hline
  705. %    Main query after                                                                                                                                                                                                                                                                                                                                                                                                                                                                          \\ \hline
  706. %    FROM (\\           select PersonID, a3.GroupID, ModuleID, ExpireTime                               \#5\\           from Students as a3,\\              relgroupsmodules as a4\\            WHERE a3.PersonId = 1\\             AND a3.GroupID = a4.GroupId\\       ) as pg                                                                                                                                                                                                                                                                                        \\ \hline
  707. %    \end{tabular}
  708. %\end{table}
  709.  
  710.         На листингах ~\ref{lst:bigq_where1_before} и ~\ref{lst:bigq_where1_after} представлены запрос до и после оптимизации, озвученной выше. Существует еще одна возможность применить оптимизацию к основному запросу:\\
  711.        
  712. \begin{lstlisting}[caption={основной запрос до II оптимизации WHERE}, label={lst:bigq_where2_before}]
  713. SELECT * FROM ( ... )
  714. UNION SELECT ... FROM ...
  715. LEFT OUTER JOIN Persons
  716.     ON Persons.PersonId = Submissions.PersonId
  717. WHERE a1.PersonId = 1
  718. \end{lstlisting}
  719.  
  720. \begin{lstlisting}[caption={основной запрос после II оптимизации WHERE}, label={lst:bigq_where2_after}]
  721. UNION SELECT ... FROM (
  722.     SELECT * FROM Approvers
  723.         WHERE PersonID = 1
  724. ) AS a1
  725. \end{lstlisting}
  726. %~\ref{lst:    }
  727.  
  728.         На листингах ~\ref{lst:bigq_where2_before} и ~\ref{lst:bigq_where2_after} применена еще одна аналогичная оптимизация WHERE.
  729. %      
  730. %\begin{table}[h]
  731. %    \caption {main1where2}
  732. %    \begin{tabular}{|l|}
  733. %    \hline
  734. %    Main query before                                                                                                                                                                                 \\ \hline
  735. %    SELECT * FROM ( ... )\\UNION SELECT ...\\LEFT OUTER JOIN Submissions \\LEFT OUTER JOIN Tasks \\\\LEFT OUTER JOIN Persons \\        ON Persons.PersonId = Submissions.PersonId \\   WHERE a1.PersonId = 1  \\ \hline
  736. %    Main query after                                                                                                                                                                                  \\ \hline
  737. %    \\\\        \\                                                                                                             \\ \hline
  738. %    \end{tabular}
  739. %\end{table}
  740.        
  741.         {
  742.             \subsubsection{Оптимизация ORDER BY / GROUP BY}
  743.         }
  744.        
  745.         При использовании GROUP BY в общем случае при выполнении запроса будет просканирована вся таблица, затем будет создана временная таблица для распределения записей по группам и применения к ним агрегирующих функций. Но, в некоторых случаях MySQL может поступить иначе, если имеет дело с индексами.
  746.        
  747.         Самым важным предусловием в данном случае является наличие индекса по всем столбцам, фигурирующим в GROUP BY. В таком случае создания дополнительной таблицы может не потребоваться и она будет заменена работой с индексным деревом.\\
  748.        
  749.        
  750.         В MySQL заложены 2 способа выполнения GROUP BY запроса с использованием индексов:
  751.         \begin{itemize}
  752.             \item[] Loose Index Scan - ``свободное'' сканирование индеса. Самый эффективный способ обработки GROUP BY - когда индекс используется чтобы выбрать столбцы группировки. MySQL может в данном случае выгодно использовать свойство хранения ключей, подразумевающее их сортировку. Это свойство позволяет выбирать группы из индекса без необходимости рассматривать все ключи, удовлетворяющие условию WHERE. Сканирование в таком случае принято называть ``свободным''. Столбцы, фигурирующие в GROUP BY при этом обязательно должны составлять префикс в каком-либо из индексов. Есть и еще одно условие - допустимы только агрегирующие функции MIN() и MAX() и ссылаются они при этом на один и тот же столбец, присутствующий в индексе и следующий непосредственно за столбцами GROUP BY. Для стоблцов должны быть созданы полноценные индексы, индексирующие значения соответствующих столбцов полностью. В случае, если все это будет выполнено, в столбце {\it Extra} соответствующей выборки будет значение {\it Using index for group-by}. Однако, добиться выполнения всех этих условий довольно сложно. В таком случае применяется Tight Index Scan.
  753.             \item[] Tight Index Scan - ``плотное'' сканирование индекса. В случае, если условия для свободного сканирования не могут быть выполнены, все еще можно добиться результата без создания дополнительной таблицы. Если в условии WHERE присутствует проверка диапазона значений, данный метод читает лишь индексы, удовлетворяющие условиям. Иначе он запускает сканирование индекса. Лишь после этих операций начинается группировка значений.
  754.         \end{itemize}
  755.        
  756.         Проверим оптимизацию на примере, в котором применяется форсированное использование индекса:\\
  757.        
  758. \begin{lstlisting}[caption={тестовый запрос до оптимизации ORDER BY}, label={lst:test_order_before}]
  759. SELECT SQL_NO_CACHE * FROM persons
  760.     HAVING FirstName LIKE '%a%'
  761.     ORDER BY FirstName;
  762. \end{lstlisting}
  763. %~\ref{lst:}
  764.  
  765. \begin{lstlisting}[caption={тестовый запрос после оптимизации ORDER BY}, label={lst:test_order_after}]
  766. CREATE INDEX PersonsFirstNameIndex ON Persons(FirstName);    
  767. SELECT SQL_NO_CACHE * FROM persons FORCE INDEX (PersonsFirstNameIndex)
  768.     HAVING FirstName LIKE '%a%'
  769.     ORDER BY FirstName;
  770. \end{lstlisting}
  771. %~\ref{lst:}
  772.  
  773.         Выполним оба запроса. Сравним результаты выполнения:
  774.  
  775. \begin{table}[h]
  776.    \caption {Сравнение таймингов запросов}
  777.    \label {test1where}
  778.    \begin{tabular}{|l|c|c|}
  779.    \hline
  780.    Запрос                  & Тайминг клиентской стороны & Тайминг серверной стророны                                                                                                                                                                                       \\ \hline
  781.    До оптимизации     & 0.0160                     & 0.0159                                                                                                                                                                                                           \\ \hline
  782.    После оптимизации  & 0.0                     & 0.0018                                                                                                                                                                                                           \\ \hline
  783.    \end{tabular}
  784. \end{table}
  785.  
  786. %
  787. %\begin{table}[h]
  788. %    \caption {test2order}
  789. %    \begin{tabular}{|l|}
  790. %    \hline
  791. %    Test query before                                                                                                                                                                                                                                                                                        \\ \hline
  792. %    SELECT SQL\_NO\_CACHE * FROM persons\\ HAVING FirstName LIKE '\%а\%'\\    ORDER BY FirstName;                                                                                                                                                                                                            \\ \hline
  793. %    Client side timing: 0.0160\\Server side timing: 0.0159                                                                                                                                                                                                                                                   \\ \hline
  794. %    Test query after                                                                                                                                                                                                                                                                                         \\ \hline
  795. %    CREATE INDEX PersonsFirstNameIndex ON Persons(FirstName); \\\\SELECT SQL\_NO\_CACHE * FROM persons FORCE INDEX (PersonsFirstNameIndex)\\   HAVING FirstName LIKE '\%а\%'\\    ORDER BY FirstName;                                                                                                          \\ \hline
  796. %    Client side timing: 0.0\\Server side timing: 0.0018                                                                                                                                                                                                                                                      \\ \hline
  797. %    \end{tabular}
  798. %\end{table}
  799.        
  800.         Как следует из результатов выше, данная оптимизация возможна. Для оптимизации ``боевого'' запроса мы можем добавить индесы на сорируемые /группируемые поля, либо добиться использования левых префиксов уже имеющихся индексов. В данном случае постараемся избавить от повторной выборки (таблицы {\it a7} и {\it a8}):\\
  801.        
  802. \begin{lstlisting}[caption={основной запрос до I оптимизации GROUP BY}, label={lst:bigq_group1_before}]
  803. SELECT ...
  804.     FROM (
  805.     SELECT DISTINCT ... FROM ...
  806.     GROUP BY GroupId, ModuleId, TaskId
  807. ) as a8
  808. \end{lstlisting}
  809. %~\ref{lst:}
  810.  
  811. \begin{lstlisting}[caption={основной запрос после I оптимизации GROUP BY}, label={lst:bigq_group1_after}]
  812. SELECT ...
  813.     FROM ( ... ) AS a7
  814. GROUP BY GroupId, ModuleId, TaskId
  815. \end{lstlisting}
  816. %~\ref{lst:}       
  817.        
  818.        
  819. %\begin{table}
  820. %    \caption {main2groupby1}
  821. %    \begin{tabular}{|l|}
  822. %    \hline
  823. %    Main query before                                                              \\ \hline
  824. %    SELECT DISTINCT ...\\FROM ...\\GROUP BY GroupId, ModuleId, TaskId \\         \\ \hline
  825. %    Main query after                                                               \\ \hline
  826. %    SELECT DISTINCT ...\\FROM ...\\... ) as a7\\GROUP BY GroupId, ModuleId, TaskId \\ \hline
  827. %    \end{tabular}
  828. %\end{table}
  829.  
  830.         Итак, мы избавлись от дублирующей выборки, как следует из Листингов ~\ref{lst:bigq_group1_before} и ~\ref{lst:bigq_group1_after}. Применим эту же оптимизацию еще раз:\\
  831.    
  832.        
  833. \begin{lstlisting}[caption={основной запрос до II оптимизации GROUP BY}, label={lst:bigq_group2_before}]
  834. (SELECT GroupId, ModuleId, max(IsStudent) AS IsStudent, max(IsApprover) AS IsApprover
  835.     FROM (
  836.         ...
  837.     ) as a10
  838.     GROUP BY GroupId, ModuleId
  839. ) as a12
  840. \end{lstlisting}
  841. %~\ref{lst:}           
  842.        
  843. \begin{lstlisting}[caption={основной запрос после II оптимизации GROUP BY}, label={lst:bigq_group2_after}]
  844. (SELECT GroupId, ModuleId, IsStudent, IsApprover
  845.     FROM WorkRole
  846.         ...
  847.     GROUP BY GroupId, ModuleId
  848. ) as a12
  849. \end{lstlisting}
  850. %~\ref{lst:}
  851.        
  852. %\begin{table}
  853. %    \caption {main2groupby2}
  854. %    \begin{tabular}{|l|}
  855. %    \hline
  856. %    Main query before                                                                                                                                              \\ \hline
  857. %    SELECT ...\\   FROM ( SELECT ... FROM ...\\        JOIN ...\\      WHERE ...\\     UNION SELECT ... FROM...\\      WHERE ...\\     ) as a10\\  GROUP BY GroupId, ModuleId \\   ) as a12 \\ \hline
  858. %    Main query after                                                                                                                                               \\ \hline
  859. %    CREATE INDEX ... ON WorkRole(GroupID, ModuleID)\\ \\SELECT ... FROM WorkRole \\WHERE ...\\ GROUP BY GroupId, ModuleId \\   ) as a12                               \\ \hline
  860. %    \end{tabular}
  861. %\end{table}
  862.  
  863.         Согласно Листингам ~\ref{lst:bigq_group2_before} и ~\ref{lst:bigq_group2_after} мы сократили количество используемых таблиц, введя дополнительную таблицу {\it WorkRole}, речь о которой подробнее будет немного позднее. Сейчас стоит лишь отметить, что в запросе задействован алгоритм группировки по индексам.
  864.        
  865.         Оптимизируем теперь и ORDER BY с одним допущением. Поскольку система T-BMSTU на данный момент используется только в МГТУ, обращение к таблице {\it Institutions} можно заменить выборкой из этой таблицы константы:\\
  866.        
  867.        
  868. \begin{lstlisting}[caption={основной запрос до оптимизации ORDER BY}, label={lst:bigq_order_before}]
  869. SELECT ... FROM ...
  870. JOIN ... ON ...
  871. ...
  872. ORDER BY
  873.     Institutions.InstitutionId ASC, ...
  874. \end{lstlisting}
  875. %~\ref{lst:}           
  876.        
  877. \begin{lstlisting}[caption={основной запрос после оптимизации ORDER BY}, label={lst:bigq_order_after}]
  878. SELECT ... FROM ...
  879. JOIN ... ON ...
  880. JOIN (SELECT institutions.InstitutionID, institutions.InstitutionName FROM ...
  881.     WHERE InstitutionID = 1) AS inst
  882. ORDER BY ...
  883. \end{lstlisting}
  884. %~\ref{lst:}
  885.  
  886. %\begin{table}
  887. %    \caption {main2orderby1}
  888. %    \begin{tabular}{|l|}
  889. %    \hline
  890. %    Main query before                                                                                                                                                                              \\ \hline
  891. %    SELECT ... FROM ...\\\\...\\JOIN ... ON ...\\\\    Institutions.InstitutionId ASC, \\  ...                                                                                 \\ \hline
  892. %    Main query after                                                                                                                                                                               \\ \hline
  893. %    SELECT ... FROM ...\\JOIN ... ON ...\\JOIN (SELECT institutions.InstitutionID, institutions.InstitutionName  FROM ...\\      WHERE InstitutionID = 1) AS inst\\JOIN ... ON ...\\ORDER BY \\... \\ \hline
  894. %    \end{tabular}
  895. %\end{table}
  896.        
  897.         Итак, мы сократили количество полей (см Листинги ~\ref{lst:bigq_order_before} и ~\ref{lst:bigq_order_after}), по которым происходит сортировка и теперь в одной из таблиц первичной выборки присутствует константная запись из таблицы, что немного ускоряет выполнение запроса.
  898.        
  899.         {
  900.                 \subsubsection{Оптимизация LIMIT X}
  901.             }      
  902.    
  903.             В некоторых случаях оптимизатор MySQL оптимизирует запрос, который имеет в своем составе LIMIT и не имеет при этом HAVING. Если в качестве лимита указано необльшое значение, MySQL вероятно предпочтет выолнить проход по индексам, в то время, как в обычном случае началось бы сканирование таблицы.
  904.            
  905.             Если LIMIT используется совместно с ORDER BY, MySQL закончит сортировку, как только наберется достаточное для LIMIT'а количество строк. В случае с DISTINCT MySQL поступит аналогичным образом.\\
  906.            
  907.             Однако, комбинация ORDER BY вместе с LIMIT 1 может быть оптимизирована. Так, запрос вида \verb|SELECT col FROM table ORDER BY col LIMIT 1| может быть заменен на \verb|SELECT MIN(col) FROM table|. В случае, если столбец проиндексирован, MySQL просто вернет минимальное значение столбца из индекса, в то время, как LIMIT+ORDER BY должны упорядоченно обойти индекс.
  908.            
  909.             Для начала проверим возможность оптимизации на таком тестовом запросе:\\
  910.            
  911. \begin{lstlisting}[caption={тестовый запрос до оптимизации LIMIT}, label={lst:test_limit_before}]
  912. SELECT SQL_NO_CACHE LoginTime FROM Sessions
  913.     WHERE PersonID BETWEEN 92 AND 109
  914.     ORDER BY LoginTime DESC
  915.     LIMIT 1;
  916. \end{lstlisting}
  917. %~\ref{lst:}
  918.  
  919. \begin{lstlisting}[caption={тестовый запрос после оптимизации LIMIT}, label={lst:test_limit_after}]
  920. SELECT SQL_NO_CACHE MAX(LoginTime) FROM Sessions
  921.     WHERE PersonID BETWEEN 92 AND 109;
  922. \end{lstlisting}
  923. %~\ref{lst:}
  924.  
  925.         Выполним оба тестовых запроса. Сравним результаты выполнения:
  926.  
  927. \begin{table}[h]
  928.    \caption {Сравнение таймингов запросов}
  929.    \label {test1where}
  930.    \begin{tabular}{|l|c|c|}
  931.    \hline
  932.    Запрос                  & Тайминг клиентской стороны & Тайминг серверной стророны                                                                                                                                                                                       \\ \hline
  933.    До оптимизации     & 0.0780                     & 0.0913                                                                                                                                                                                                           \\ \hline
  934.    После оптимизации  & 0.0470                     & 0.0501                                                                                                                                                                                                           \\ \hline
  935.    \end{tabular}
  936. \end{table}
  937.  
  938. %          
  939. %\begin{table}
  940. %    \caption {test3limit}
  941. %    \begin{tabular}{|l|}
  942. %    \hline
  943. %    Test query before                                                                                                                                                                                                                                                                                        \\ \hline
  944. %    SELECT SQL\_NO\_CACHE LoginTime FROM Sessions\\    WHERE PersonID BETWEEN 92 AND 109\\ ORDER BY LoginTime DESC\\    LIMIT 1;                                                                                                                                                                                \\ \hline
  945. %    Client side timing: 0.0780\\Server side timing: 0.0913                                                                                                                                                                                                                                                   \\ \hline
  946. %    Test query after                                                                                                                                                                                                                                                                                         \\ \hline
  947. %    SELECT SQL\_NO\_CACHE MAX(LoginTime) FROM Sessions\\   WHERE PersonID BETWEEN 92 AND 109;                                                                                                                                                                                                                  \\ \hline
  948. %    Client side timing: 0.0470\\Server side timing: 0.0501                                                                                                                                                                                                                                                   \\ \hline
  949. %    \end{tabular}
  950. %\end{table}
  951.            
  952.             Эта оптимизация дает незначительные преимущества в случае индексированных столбцов и небольшой выигрыш в случае отсутствия индексов. Оптимизация присутствует на Листингах ~\ref{lst:test_limit_before} и ~\ref{lst:test_limit_after}.
  953.            
  954.             Применим эту оптимизацию:\\
  955.            
  956.            
  957. \begin{lstlisting}[caption={основной запрос до оптимизации LIMIT}, label={lst:bigq_limit_before}]
  958. SELECT Accepted FROM approvements a6
  959.     JOIN submissions
  960.     JOIN relgroupsmodules a4
  961.     WHERE a6.SubmissionId = Submissions.SubmissionId
  962.         AND (a4.ExpireTime IS NULL
  963.         OR Submissions.SubmissionTime < a4.ExpireTime)
  964.     ORDER BY ApprovementTime DESC
  965.     LIMIT 1;
  966. \end{lstlisting}
  967. %~\ref{lst:}
  968.  
  969. \begin{lstlisting}[caption={основной запрос после оптимизации LIMIT}, label={lst:bigq_limit_after}]
  970. SELECT MAX(Accepted) FROM approvements a6
  971.     JOIN submissions
  972.     JOIN relgroupsmodules a4
  973.     WHERE a6.SubmissionId = Submissions.SubmissionId
  974.         AND (a4.ExpireTime IS NULL
  975.         OR Submissions.SubmissionTime < a4.ExpireTime)
  976.         AND ApprovementTime = (SELECT MAX(ApprovementTime)
  977.                     FROM approvements);
  978. \end{lstlisting}
  979. %~\ref{lst:}
  980.  
  981.  
  982. %\begin{table}
  983. %    \caption {main3limi1}
  984. %    \begin{tabular}{|l|}
  985. %    \hline
  986. %    Main query before                                                                                                                                                                                                                                                                                                                   \\ \hline
  987. %    SELECT Accepted \\ FROM approvements a6\\    JOIN submissions\\    JOIN relgroupsmodules a4\\  WHERE a6.SubmissionId = Submissions.SubmissionId \\     AND (a4.ExpireTime IS NULL \\           OR Submissions.SubmissionTime < a4.ExpireTime) \\   ORDER BY ApprovementTime DESC \\    LIMIT 1;                                                      \\ \hline
  988. %    Main query after                                                                                                                                                                                                                                                                                                                    \\ \hline
  989. %    SELECT MAX(Accepted)\\ FROM approvements a6\\    JOIN submissions\\    JOIN relgroupsmodules a4\\    WHERE a6.SubmissionId = Submissions.SubmissionId \\       AND (a4.ExpireTime IS NULL \\           OR Submissions.SubmissionTime < a4.ExpireTime) \\        AND ApprovementTime = (SELECT MAX(ApprovementTime) \\                              FROM approvements); \\ \hline
  990. %    \end{tabular}
  991. %\end{table}
  992.  
  993.         Доступна еще одна оптимизация в запросе, подобная изложенной на Листингах ~\ref{lst:bigq_limit_before} и ~\ref{lst:bigq_limit_after} . Опустим ее, так как их механики в запросе идентичны с точностью до знаков.
  994.    
  995.             {
  996.                 \subsubsection{Оптимизация UNION и DISTINCT}
  997.             }
  998.            
  999.         Преобразование UNION в UNION ALL в запросе дает большую выгоду. Первым предположением является то, что это достигается за счет того, что UNION ALL'у не нужна дополнительная таблица для хранения результата, однако это не совсем верно. Обе формы объединения используют временную таблицу для генерации результата.
  1000.        
  1001.         Интересен тот факт, что создание этой временной таблицы можно посмотреть с помощью SHOW STATUS. В обычном EXPLAIN'е этой дейстиве по-умолчанию скрыто.
  1002.        
  1003.         Отличием же в выполнении этих запросов является то, что обычный UNION создает промежуточную таблицу, накладывает на нее индекс и лишь после этого приступает к выборке, удаляя дубликаты. В то же время UNION ALL пропускает эти действия, за счет чего и достигается повышение производительности.\\
  1004.        
  1005.         Проверим этот факт, выполнив запрос один раз с  объединением UNION, другой - с объединением UNION ALL, как показано на Листингах ~\ref{lst:test_union_before} и ~\ref{lst:test_union_after}:\\
  1006.        
  1007.  
  1008. \begin{lstlisting}[caption={тестовый запрос до оптимизации UNION}, label={lst:test_union_before}]
  1009. SELECT DISTINCT personID, groupID, moduleID, taskID, SubmissionTime
  1010.     FROM Submissions WHERE PassedTests IS NOT NULL
  1011. UNION
  1012. SELECT DISTINCT personID, groupID, moduleID, taskID, SubmissionTime
  1013.     FROM Submissions WHERE PassedTests IS NULL;
  1014. \end{lstlisting}
  1015. %~\ref{lst:}
  1016.  
  1017. \begin{lstlisting}[caption={тестовый запрос после оптимизации UNION}, label={lst:test_union_after}]
  1018. SELECT DISTINCT personID, groupID, moduleID, taskID
  1019.     FROM Submissions WHERE PassedTests IS NOT NULL
  1020. UNION ALL
  1021. SELECT DISTINCT personID, groupID, moduleID, taskID
  1022.     FROM Submissions WHERE PassedTests IS NOT NULL;
  1023. \end{lstlisting}
  1024. %~\ref{lst:}
  1025.  
  1026.         Выполним оба тестовых запроса. Сравним результаты выполнения:
  1027.  
  1028. \begin{table}[h]
  1029.    \caption {Сравнение таймингов запросов}
  1030.    \label {test1where}
  1031.    \begin{tabular}{|l|c|c|}
  1032.    \hline
  1033.    Запрос                  & Тайминг клиентской стороны & Тайминг серверной стророны                                                                                                                                                                                       \\ \hline
  1034.    До оптимизации     & 0.1400                     & 0.1337                                                                                                                                                                                                           \\ \hline
  1035.    После оптимизации  & 0.0780                     & 0.0657                                                                                                                                                                                                           \\ \hline
  1036.    \end{tabular}
  1037. \end{table}
  1038.  
  1039. %\begin{table}
  1040. %    \caption {test4union}
  1041. %    \begin{tabular}{|l|}
  1042. %    \hline
  1043. %    Test query before                                                                                                                                                                                                                                                                                        \\ \hline
  1044. %    SELECT DISTINCT personID, groupID, moduleID, taskID, SubmissionTime\\  FROM Submissions WHERE PassedTests IS NOT NULL \\UNION \\SELECT DISTINCT personID, groupID, moduleID, taskID, SubmissionTime\\  FROM Submissions WHERE PassedTests IS NULL;                                                         \\ \hline
  1045. %    Client side timing: 0.1400\\Server side timing: 0.1337                                                                                                                                                                                                                                                   \\ \hline
  1046. %    Test query after                                                                                                                                                                                                                                                                                         \\ \hline
  1047. %    SELECT DISTINCT personID, groupID, moduleID, taskID \\ FROM Submissions WHERE PassedTests IS NOT NULL \\UNION ALL\\SELECT DISTINCT personID, groupID, moduleID, taskID \\  FROM Submissions WHERE PassedTests IS NOT NULL;                                                                                \\ \hline
  1048. %    Client side timing: 0.0780\\Server side timing: 0.0657                                                                                                                                                                                                                                                   \\ \hline
  1049. %    \end{tabular}
  1050. %\end{table}
  1051.        
  1052.         Основным моментом здесь является то, что в обоих запросах присутствуют ключевые слова DISTINCT. А значит, по схеме выполнения MySQL сначала сделает выборку из одной таблицы, отфильтровав дубликаты, затем поступит по аналогии со второй таблицей. Затем в запросе с обычным UNION, после объединения выборок во временную таблицу, MySQL добавит индексы и вновь отфильтрует результаты, пройдя по таблице. Однако в таблице к тому моменту уже не будет дубликатов и этот проход будет лишним.
  1053.        
  1054.         Избавившись от него с помощью использования UNION ALL, мы получим выигрыш в производительности.
  1055.        
  1056.         Стоит также заметить, что как и в случае с GROUP BY / ORDER BY, MySQL может использовть лишь левые префиксы индексов для работы с ключами, и в целом DISTINCT иногда рассматривается оптимизатором, как частный случай GROUP BY.
  1057.    
  1058.             Выполним описанные выше преобразования, так как аналогичные условия имеются в рассматриваемом нами запросе и запишем новые запросе в Листинги ~\ref{lst:bigq_union_before} и ~\ref{lst:bigq_union_after}:\\
  1059.  
  1060.  
  1061. \begin{lstlisting}[caption={основной запрос до оптимизации UNION}, label={lst:bigq_union_before}]
  1062. SELECT DISTINCT ... FROM ... AS a8
  1063. UNION
  1064. SELECT DISTINCT ... FROM ... AS a1
  1065. \end{lstlisting}
  1066. %~\ref{lst:}
  1067.  
  1068. \begin{lstlisting}[caption={основной запрос после оптимизации UNION}, label={lst:bigq_union_after}]
  1069. SELECT DISTINCT ... FROM ... AS a7
  1070. UNION ALL
  1071. SELECT DISTINCT ... FROM ... AS a1
  1072. \end{lstlisting}
  1073. %~\ref{lst:}    
  1074.  
  1075.        
  1076. %\begin{table}
  1077. %    \caption {main4union1}
  1078. %    \begin{tabular}{|l|}
  1079. %    \hline
  1080. %    Main query before                                                                 \\ \hline
  1081. %    SELECT DISTINCT ... FROM ... AS a8 \\UNION \\SELECT DISTINCT ... FROM ... AS a1   \\ \hline
  1082. %    Main query after                                                                  \\ \hline
  1083. %    SELECT DISTINCT ... FROM ... AS a7\\UNION ALL\\SELECT DISTINCT ... FROM ... AS a1 \\ \hline
  1084. %    \end{tabular}
  1085. %\end{table}
  1086.    
  1087.    
  1088.             {
  1089.                 \subsubsection{Оптимизация LEFT и RIGHT JOIN}
  1090.             }
  1091.    
  1092.             MySQL выполняет объединение таблиц, например \verb|A LEFT JOIN B| подобным образом:
  1093.             \begin{itemize}
  1094.                 \item[] Таблица B устанавливается зависимой от A и от всех таблиц, от которых зависит A. Таблица A в свою очередь устанваливается зависимой от всех таблиц, кроме B, которые используются в условии LEFT JOIN
  1095.                 \item[] Используется условие LEFT JOIN для того, чтобы решить, как выбрать записи из таблицы B
  1096.                 \item[] Выполняются все стандартные оптимизации входящих внутрь запроса условий
  1097.                 \item[] В случае, если в таблице B запись, соответствующая условию ON, и для которой в таблице A имеется запись, отсутствует, то в таблицу B дописывается запись со всеми столбцами, равными NULL
  1098.                 \item[] Новая запись в таблице со всеми параметрами, равными NULL, добавляется в результирующей выборке в соответствие непустой записи из таблицы A
  1099.         \end{itemize}
  1100.        
  1101.         Реализация RIGHT JOIN аналогична с точностью до порядка таблиц. Оптимизацией JOIN запросов является перестановка таблиц в запросах. Однако LEFT JOIN и STRAIGHT JOIN практически не оптимизируются.
  1102.        
  1103.         Еще одна форма объединения - STRAIGHT JOIN. Эта команда не дает оптимизатору выбора, кроме как принять порядок, заданный пользователем, вследствие чего никакие дополнительные преобарзования не производятся и запрос за счет этого ускоряется. Как и в остальных случаях упор делается на наличие индексов. Достигается это улучшение только в том, случае, если известно, что ``навязанный'' оптимзатору порядок соединения однозначно лучше чем тот, который может выбрать он сам. В большинстве случаев ``навязывание'' подобных условий оптимизатору может привести к обратному результату.\\
  1104.    
  1105.             Навязвание использования индексов совместно с ``непереставляемым'' LEFT JOIN'ом уже рассматривалось ранее. Чтобы избежать дублирования, опустим этот момент здесь.
  1106.    
  1107.             {
  1108.                 \subsubsection{Оптимизация IS (NOT) NULL}
  1109.             }
  1110.            
  1111.             MySQL оптимизатор умеет удалять ненужны проверки на наличие /отсутстивие NULL значений. Так, если условие WHERE содержит проверку столбца {\it a} IS NULL при том, что сам столбец объявлен как NOT NULL, проверка будет удалена.
  1112.            
  1113.             Также, согласно best practices, не рекомендуется использовать значения типа NULL на столбцах типов DATE, TIME и DATETIME. Убрав поддержку NULL значений и заменив ее сравнением с ``новым отсутствующим'' значением, например стандартной NULL-датой 0000-00-00 00:00:00, обеспечим выолнение best practices в рамках таблицы. Оптимизация представлена на Листингах ~\ref{lst:bigq_null_before} и ~\ref{lst:bigq_null_after}:\\
  1114.            
  1115. \begin{lstlisting}[caption={основной запрос до оптимизации IS (NOT) NULL}, label={lst:bigq_null_before}]
  1116. (SELECT Accepted
  1117.     FROM Approvements as a6
  1118.     WHERE a6.SubmissionId = Submissions.SubmissionId
  1119.         AND (a4.ExpireTime IS NULL
  1120.             OR Submissions.SubmissionTime < a4.ExpireTime)
  1121. \end{lstlisting}
  1122. %~\ref{lst:}
  1123.  
  1124. \begin{lstlisting}[caption={основной запрос после оптимизации IS (NOT) NULL}, label={lst:bigq_null_after}]
  1125. (SELECT Accepted
  1126.     FROM Approvements as a6
  1127.     WHERE a6.SubmissionId = Submissions.SubmissionId
  1128.         AND (a4.ExpireTime > '0000-00-00 00:00:00'
  1129.             OR Submissions.SubmissionTime < a4.ExpireTime)
  1130. \end{lstlisting}
  1131. %~\ref{lst:}  
  1132.  
  1133. %\begin{table}
  1134. %    \caption {main6null1}
  1135. %    \begin{tabular}{|l|}
  1136. %    \hline
  1137. %    Main query before                                                                                                                                                                                                                                                            \\ \hline
  1138. %    SELECT Accepted \\FROM Approvements as a5\\WHERE a5.SubmissionId = Submissions.SubmissionId \\AND a4.ExpireTime IS NOT NULL AND Submissions.SubmissionTime >= a4.ExpireTime \\ORDER BY ApprovementTime DESC \\LIMIT 1                                                        \\ \hline
  1139. %    Main query after                                                                                                                                                                                                                                                             \\ \hline
  1140. %    SELECT Accepted                                                                \#7\\               FROM Approvements as a5\\               WHERE a5.SubmissionId = Submissions.SubmissionId \\                 AND a4.ExpireTime > '0000-00-00 00:00:00' \\AND Submissions.SubmissionTime >= pg.ExpireTime \\              ORDER BY ApprovementTime DESC \\                LIMIT 1  \\ \hline
  1141. %    \end{tabular}
  1142. %\end{table}           
  1143.            
  1144.         {
  1145.                 \subsection{MySQL и индексы}
  1146.             }  
  1147.    
  1148.             Лучший способ улучшить производительность операции SELECT это создать индексы на одном или нескольких столбцах, используемых в запросе. Индексы выступают в качетсве указателей на записи, позволяя быстро определить какие записи подходят под условие WHERE и выбрать остальные поля записи.
  1149.            
  1150.             Все типы данных в MySQL могут быть проиндексированы. Все индексы в MySQL в рамках движка InnoDB, будь то PRIMARY, UNIQUE и INDEX, сохранены в B-беревьях. Индексные страницы при этом хранятся вместе с данными. MyISAM же использует для этих целей хеш-таблицы.
  1151.            
  1152.             И, хотя может возникнуть желание создать индексы по всем возможным столбцам, неиспользуемые и ненужные ндексы занимают место и отнимают у оптимизатора  MySQL время на поиск необходимиого ему оптимального индекса.
  1153.            
  1154.             Индексы также увеличивают ``стоимость'' операций вставки, удаления и обновления, так как каждый индекс должен быть обновлен.
  1155.    
  1156.             MySQL поддерживает до 16 ключей на одной таблице.
  1157.            
  1158.             Стобцы типов BLOB и TEXT поддерживают неполное индексирование. На столбцах типов CHAR и VARCHAR разрешено создавать частичные индексы, которые при этом могут составлять часть многостолбцового индекса.
  1159.            
  1160.             Как уже оговаривалось ранее, только крайние левые префиксы индекса могут быть использованы большинством операций. Однако, иногда выборка может быть произведена совсем без обращения к данным. Это происходит в том случае, если выбирается часть индекса по условию другой части индекса. Так, трехстолбцовый индекс на столбцах {\it (a, b, c, d)} дает поисковые преимущества в таких сочетаниях: {\it(a), (a, b), (a, b, c)}. При выборке же можно использовать столбец {\it c} в то время, как условие наложено на столбец {\it a} или {\it b}.\\
  1161.    
  1162.             Ранее было отмечено, что данные большинства таблиц в исходной базе данных редактируются нечасто. Поэтому, на них можно без ограничений накладыватьь индексы. В базе присутствуют лишь 4 постоянно используемых таблицы, которые были отмечены ранее. На этих таблицах уже присутствуют индексы помимо PRIMARY и UNIQUE. Эти индексы, действительно, используются оптимизатором и ``утяжелять'' таблицы дополнительными индексами мы не будем.
  1163.            
  1164.             Вместо этого добавим несколько индексов, возможное отсутствие некоторых из которых приводило к полму сканированию таблиц согласно первому результату EXPLAIN'а:\\
  1165.            
  1166.             \begin{verbatim}
  1167. CREATE INDEX InstitutionIDInstitutionNameIndex
  1168.             ON Institutions(InstitutionID, InstitutionName);
  1169. CREATE INDEX GroupInstitutionTimeNameIndex
  1170.             ON Groups (GroupID, InstitutionID, TimeID, GroupName);
  1171. CREATE INDEX GroupIndex ON Approvers (GroupID);
  1172. CREATE UNIQUE INDEX RelGroupsModulesExpireTimeGroupIDModuleIDUniqueIndex
  1173.             ON RelGroupsModules(ExpireTime, GroupID, ModuleID);
  1174.         \end{verbatim}
  1175.    
  1176.            
  1177.             {
  1178.                 \subsection{Другие оптимизации}
  1179.             }  
  1180.            
  1181.             {
  1182.                 \subsubsection{Выбор движка базы данных}
  1183.             }  
  1184.            
  1185.             Одной из особенностей MySQL является наличие большого числа движков, практически каждый из которых специализирован под конкретную задачу. Предполагается, что выбор движка происходит на этапе проектирования. Перечислим основные движки и озвучим их особенности:
  1186.            
  1187.             \begin{itemize}
  1188.                 \item[] MyISAM - не поддерживает транцзакции, но поддерживает полнотекстовый поиск. Внешние ключи недоступны. Данные и индексы хранятся отдельно. Сравнительно невысокая надежность хранения данных
  1189.                 \item[] Memory (ранее, HEAP) - отличается несравнимо быстрой работой с небольшими таблицами, так как хранит все временные таблицы в оперативной памяти. Практически не имеет конкурентов по скорости работы
  1190.                 \item[] Federated - федерация серверов, обеспечивающая высокую масштабируемость и высокую отказоустойчивость
  1191.                 \item[] CSV - хранит таблицы в CSV формате и позволяет редактировать их внешними приложениями. Отличается также невысокой стаблильнотсью работы
  1192.                 \item[] InnoDB - движок для таблиц ``общего'' назначения, тем не менее поддерживающий большие таблицы. Полная поддержка транзакций (ACID), внешних ключей. Максимальный объем - 64ТБ. По заявлениям разработчиков InnoDB - самый быстрый основанный ``на диске'' движок. Однако, сильно зависит от надлежащей индексации данных
  1193.                 \item[] Blackhole - движок, созданный для задач репликации.Не умеет самостоятельно хранить данные. Может выстпуть ``мастером'' в схеме репликации master-slave
  1194.                 \item[] Example - экспериментальный движок для разработчиков. Таблицы, основанные на нем не могут хранить данные и нужны в первую очередь для создания новых типов таблиц.
  1195.             \end{itemize}
  1196.            
  1197.             Как было замечено ранее, в работе был выбран движок InnoDB по причине поддержки внешних ключей, транзакций и других функций ``из коробки''.
  1198.            
  1199.            
  1200.             {
  1201.                 \subsubsection{Оптимизация некоторых типов данных}
  1202.             }  
  1203.    
  1204.             Одним из способов измерения производительности запросов MySQL называет измерение количества дисковых операций. Для небольших таблиц обычно можно найти строку одним обращением. Для больших таблиц, использующих дерево индексов, количество дисковых операции можно оценить формулой
  1205.            
  1206.             $$C=\frac{\ln row\_count}{\ln {\frac{index\_block\_length \times 2}{3 \times (index\_length + data\_pointer\_length)}}}$$
  1207.            
  1208.             Индексный блок обычно составляет 1024 байта, указатель - 4 байта.
  1209.            
  1210.             Можем оценить количество дисковых операций для таблицы {\it Submissions}:
  1211.            
  1212.             $$C=\frac{\ln 130'000}{\ln {\frac{1'024 \times 2}{3 \times (16 + 4)}}} = \frac{11.775}{3.530} \approx 3$$
  1213.            
  1214.             Итого, потребуется в среднем 3 дисковых операции для того, чтобы найти конкретную строку в таблице {\it Submissions}, вмещающей 130'000 записей при наличии на ней индекса длины 16.
  1215.            
  1216.             Логарифмическая зависимость объема таблицы позволяет оценить незначительность количества дисковых операций для большинства таблиц базы. Взяв абстрактную таблицу, содержащую, например, 300 записей и имеющую индекс длины 4 на столбце типа INTEGER выясним, что записи из подобных таблиц выбираются за одно обращение. Это еще раз подтверждает возможность наложения дополнительных индексов на подобные таблицы:
  1217.            
  1218.             $$C=\frac{\ln 300}{\ln {\frac{1'024 \times 2}{3 \times (4 + 4)}}} \approx 1$$
  1219.            
  1220.             И хотя в наше время скорость работы важнее объема затраченной памяти, в MySQL  рекомендуется содержать данные компактно. А значит, следуют и небольшие оптимизации некоторых таблиц базы данных:\\
  1221.            
  1222.             \begin{itemize}
  1223.                 \item[] TINYINT или MEDIUMINT препочтительнее ``обчыного'' типа INT, если это не проиворечит логике работы
  1224.                 \item[] NULL требует дополнительного места, а значит, ограничения в виде NOT NULL значений немного уменьшат место. Это преимущество в рассамтриваемой базе незначительно ввиду небольшого общего объема данных
  1225.                 \item[] Вынос логики работы с величинами времени и даты во вне. В базе при этом можно оставить значения типа TIMESTAMP или INT. Эта оптимизация значительнее предыдущих и может дать преимущество в несколько раз.
  1226.             \end{itemize}
  1227.            
  1228.             {
  1229.                 \subsubsection{Оптимизация базы данных}
  1230.             }
  1231.            
  1232.             В ходе рассмторения плана выполнения запроса было выявлено большое количество подзапросов, в том числе  с полным сканированием таблиц. Один из таких запросов, с {\it id=1} отмечен как {\it derived 2}, ссылающийся на подзапрос с {\it id=2}. Тот в свою очередь отмечен, как {\it derived 3}. Подзапрос с {\it id=3} представляет из себя 2 подзапроса - {\it a11} и {\it RelGroupsModules}. После этого выполняется выборка из таблицы {\it a9}. И лишь после этого происходит объединение этих подзапросов в результирующую выборку. Все это сочетается с неопределенностью ключей для всех этих выборок а также постоянным сканированием таблиц.
  1233.            
  1234.             Это можно исправить путем создания дополнительной таблицы. Назовем ее WorkRole. В нее перенесем функциональность под разделению ролей пользователей на студентов и принимающую их сторону:\\
  1235.            
  1236. \begin{lstlisting}[caption={Скрипт создания таблицы WorkRole}, label={lst:table_workrole}]
  1237. CREATE TABLE WorkRole (
  1238.    PersonID        INTEGER NOT NULL REFERENCES Persons
  1239.                            ON DELETE CASCADE ON UPDATE CASCADE,
  1240.    GroupID         INTEGER NOT NULL REFERENCES Groups
  1241.                            ON DELETE CASCADE ON UPDATE CASCADE,
  1242.    ModuleID        INTEGER NOT NULL REFERENCES Modules
  1243.                            ON DELETE CASCADE ON UPDATE CASCADE,
  1244.    
  1245.    IsStudent       TINYINT NOT NULL,
  1246.    IsApprover      TINYINT NOT NULL,
  1247.  
  1248.    PRIMARY KEY (PersonID, GroupID, ModuleID, IsStudent)
  1249. );
  1250. \end{lstlisting}
  1251.            
  1252.             Наличие ключа, состоящего из 4 столбцов объясняется спецификой выборки данных в запросе. Таблица носит  технический характер и создана с целью сбора данных для запроса. При этом необходимо наличие PRIMARY индекса в подобном порядке, чтобы впоследствие использовать его также как и в изначальной версии запроса.
  1253.  
  1254. Создадим дополнительные индексы для выборки непосредственно ролей (так как в PRIMARY индексе при отсутствии выборки по префиксу {\it PersonID, GroupID, ModuleID}, исполнитель не сможет оптимально работать со столбцами {\it IsStudent} и {\it IsApprover}:\\
  1255.  
  1256. \begin{verbatim}
  1257. CREATE INDEX WorkRoleIsStudentIndex ON WorkRole(PersonID, IsStudent);
  1258. CREATE INDEX WorkRoleIsApproverIndex ON WorkRole(PersonID, IsApprover);
  1259. \end{verbatim}
  1260.  
  1261.         Чтобы не повлиять на текущую функциональность базы, все изначально имеющиеся в ней таблице редактировать не будем. Для работы же с этой вновь созданной таблицей создадим необходимые триггеры, которые изменяют данные внутри {\it WorkRole} основываясь на изменении данных в ``базовых'' для нее таблицах {\it Students} и {\it Approvers}.
  1262.        
  1263.         Перепишем ту часть запроса, которая некогда выполняла описанные выше действия, изменив порядок выполнения на работу с новой таблицей WorkRole, согласно Листингам ~\ref{lst:bigq_workrole_before} и ~\ref{lst:bigq_workrole_after}:\\
  1264.  
  1265. \begin{lstlisting}[caption={основной запрос до внедрения таблицы WorkRole}, label={lst:bigq_workrole_before}]
  1266. SELECT GroupId, ModuleId, max(IsStudent) AS IsStudent, max(IsApprover) AS IsApprover
  1267.     FROM (
  1268.         SELECT a11.GroupId, ModuleId, 1 AS IsStudent, 0 AS IsApprover
  1269.             FROM Students as a11
  1270.             JOIN RelGroupsModules USING(GroupId)
  1271.             WHERE PersonId = 1
  1272.         UNION
  1273.         SELECT GroupId, ModuleId, 0 AS IsStudent, 1 AS IsApprover
  1274.             FROM Approvers as a9
  1275.             WHERE PersonId = 1
  1276.         ) as a10
  1277.     GROUP BY GroupId, ModuleId
  1278. \end{lstlisting}
  1279. %~\ref{lst:}
  1280.  
  1281. \begin{lstlisting}[caption={основной запрос после внедрения таблицы WorkRole}, label={lst:bigq_workrole_after}]
  1282. SELECT GroupId, ModuleId, IsStudent, IsApprover
  1283.     FROM WorkRole
  1284.     WHERE PersonID = 1
  1285.     GROUP BY GroupId, ModuleId
  1286. \end{lstlisting}
  1287. %~\ref{lst:}       
  1288.  
  1289.  
  1290. %\begin{table}
  1291. %    \caption {WorkRole}
  1292. %    \begin{tabular}{|l|}
  1293. %    \hline
  1294. %    Main query before                                                                                                                                                                                                                                                                                                                                                                                                      \\ \hline
  1295. %    SELECT GroupId, ModuleId, max(IsStudent) AS IsStudent, max(IsApprover) AS IsApprover \\    FROM ( \\       SELECT a11.GroupId, ModuleId, 1 AS IsStudent, 0 AS IsApprover \\        FROM Students as a11\\      JOIN RelGroupsModules USING(GroupId) \\     WHERE PersonId = 1 \\       UNION SELECT GroupId, ModuleId, 0 AS IsStudent, 1 AS IsApprover \\      FROM Approvers as a9\\      WHERE PersonId = 1 \\       ) as a10\\  GROUP BY GroupId, ModuleId  \\ \hline
  1296. %    Main query after                                                                                                                                                                                                                                                                                                                                                                                                       \\ \hline
  1297. %    SELECT GroupId, ModuleId, IsStudent, IsApprover\\  FROM WorkRole \\    WHERE PersonID = 1\\    GROUP BY GroupId, ModuleId                                                                                                                                                                                                                                                                                                  \\ \hline
  1298. %    \end{tabular}
  1299. %\end{table}
  1300.  
  1301.         \bigskip
  1302.            
  1303.             {
  1304.                 \section{ТЕСТИРОВАНИЕ}
  1305.             }  
  1306.            
  1307.             {
  1308.                 \subsection{Сравнение производительности}
  1309.             }
  1310.            
  1311.             На данный момент имеется оптимизированный по описанным выше пунктам запрос. Некоторые изменения в ходе работы коснулись и самой базы данных. Она также была оптимизирована.
  1312.            
  1313.             Все проводимые оптимизации не затрагивали результат выборки основоного запроса. Так что исходный запрос и получившийся в итоге, оптимизированный, можно считать идентичными по своей функциональности.
  1314.            
  1315.         Взглянем на план выполнения нового запроса
  1316.        
  1317.         %вставить новый эксплэин
  1318.         <здесь новый EXPLAIN>
  1319.        
  1320.             Вот что изменилось по сравнению с предыдущим выполнением EXPLAIN'а:
  1321.                 \begin{itemize}
  1322.                     \item[] Остались 2 полнотекстовых сканирования, вместо 6 первоначальных
  1323.                     \item[] Убрана неиспользуемая временная выборка, дублирующая подзапрос ({\it id = 4})
  1324.                     \item[] Остались лишь 7 подзапросов вместо 10 первоначальных
  1325.                     \item[] Перва выборка имеет максимально быстрый (после ({\it type=system}) тип объединения, а длина используемого ключа сокращена
  1326.                     \item[] Уменьшено общее количество участвующих в запросе строк
  1327.                 \end{itemize}
  1328.                
  1329.         Согласно результатам EXPLAIN'ов, запрос, действительно, стал работать оптимальнее. В качестве результата представим таблицу профилирования запросов инструментом MySQL Profiler, которая не учитывает время подключения к базе данных, время ожидания открытия и закрытия таблиц, завершения других процессов и т.д., а показывает идеализированное ``чистое'' время выполнения:
  1330.        
  1331. \begin{table}[h]
  1332.    \caption {test1profiling}
  1333.    \begin{tabular}{|c|c|c|}
  1334.    \hline
  1335.    Тип действия         & Тайминг исходного запроса & Тайминг полученного запроса\\ \hline
  1336.     executing          & 0.000001      & 0.000001        \\
  1337.    Sending data        & 0.000005      & 0.000005        \\
  1338.    executing           & 0.000001      & 0.000001        \\
  1339.    Sending data        & 0.000005      & {\bf 0.000016 }       \\
  1340.    executing           & 0.000001      & 0.000001        \\
  1341.    Sending data        & 0.000005      & 0.000006        \\ \hline
  1342.    …                   & …             & …               \\ \hline
  1343.    Sending data        & 0.000006      & {\bf 0.000035  }      \\
  1344.    executing           & 0.000001      & 0.000002        \\
  1345.    Sending data        & {\bf 0.005742}      & 0.000046        \\
  1346.    executing           & 0.000003      & 0.000001        \\
  1347.    Sending data        & {\bf 0.031268}      & 0.023095        \\
  1348.    Creating sort index & {\bf 0.014082}      & 0.013395        \\
  1349.    end                 & 0.000011      & 0.000012        \\ \hline
  1350.    …                   & …             & …               \\ \hline
  1351.    query end           & 0.000013      & 0.000013        \\
  1352.    removing tmp table  & 0.000175      & {\bf 0.000201}        \\
  1353.    closing tables      & 0.000002      & 0.000004        \\
  1354.    removing tmp table  & 0.000004      & 0.000003        \\
  1355.    closing tables      & 0.000001      & 0.000005        \\
  1356.    freeing items       & 0.000127      & {\bf 0.000139}        \\
  1357.    cleaning up         & 0.000017      & 0.000018        \\ \hline
  1358.    \end{tabular}
  1359. \end{table}
  1360.        
  1361.             Опустим бОльшую часть таблицы резульата, оставив только начало и наиболее отличительные моменты. Общее время выполнения первого запроса составляет около 0.06 секунды согласно профайлеру. Время выполнения второго, оптимизированного запроса составляет порядка 0.04 секунды. Как видно из результата, наибольший выигрыш происходит при отправке меньшего количества данных на сортировку - отправка отрабатывает почти на треть быстрее. Одна из введенных нами оптимизаций позволила не отправлять также значительную часть данных, в отличие от первого запроса, благодаря чему мы также получили выигрыш в производительности. Однако, среднее время ``уборки'' после запроса незначительно выросло, как следует из нижней части результата - действий после завершения формирования выборки.
  1362.             При сравнении результатов запросов в SQLite и MySQL ситуация аналогичная с точностью до затрат по подключению непосредственно к базам данных (из утилиты основанной на ADO.Net). Это объясняется нивелированием преимуществ одной СУБД над другой за счет оптимизации общей их части - механики выполнения запросов, которая по большей части идентична. Результат закономерен.
  1363.            
  1364.             Выполним ``проверку статусов'' SELECT запросом \verb|SESSION STATUS LIKE `Select\%'|:
  1365.            
  1366. \begin{table}[h]
  1367.    \caption {test2selects}
  1368.    \begin{tabular}{|c|c|c|}
  1369.    \hline
  1370.    Тип призведенного действия         & Исходный запрос & Новый запрос \\ \hline
  1371.    Select\_full\_join          & 1      & 0        \\
  1372.    Select\_range\_check           & 0      & 0        \\
  1373.    Select\_range           & 0      & 0        \\
  1374.    Select\_scan        & 6      & 2        \\ \hline
  1375.    \end{tabular}
  1376. \end{table}
  1377.  
  1378.     Эти результаты мы могли наблюдать через EXPLAIN. Общее количество сканирований сократилось.
  1379.    
  1380.     Выполним проверку создания временных таблиц \verb|SESSION STATUS LIKE `Created_tmp\%'|:
  1381.  
  1382. \begin{table}[h]
  1383.    \caption {test3temporaries}
  1384.    \begin{tabular}{|c|c|c|}
  1385.    \hline
  1386.    Тип произведенного действия         & Исходный запрос & Новый запрос \\ \hline
  1387.    Created\_tmp\_files          & 4      & 4        \\
  1388.    Created\_tmp\_tables          & 12      & 8        \\ \hline
  1389.    \end{tabular}
  1390. \end{table}
  1391.  
  1392.             Из результата этой выборки также видно уменьшение количества временно созданных таблиц с 12 до 8.
  1393.            
  1394.             {
  1395.                 \subsection{Оценка оптимизации}
  1396.             }
  1397.            
  1398.         Оптимизация, выполненная над базой не включала в себя масштабные изменения. Структура базы практически не затронута. Все изначально существовавшие таблицы сохранены, представления сохранены. Триггеры адаптированы и сохранены. Рефакторинг структуры базы не был произведен ввиду необходимости сохранить ее функционирующее состояние.
  1399.        
  1400.         Изначально спроектированная база данных достаточно нормализована, не содержит избыточных данных, многозначных и многоцелевых столбцов. На данный момент в базе данных присутствует сравнительно небольшой объем данных. Основываясь на всем этом можно сделать вывод, что имеющаяся база данных в рефакторинге не нуждается, по крайней мере на данный момент.
  1401.        
  1402.             Переход с SQLite на MySQL является спорным моментом, так как одним из плюсов СУБД SQLite является ее простота. Большинство преимуществ MySQL над SQLite в ходе данной работы задействованы не были и в целом являются преимуществами в специфических условиях.
  1403.            
  1404.            
  1405.             \clearpage
  1406.  
  1407.             {
  1408.                 \section{ЗАКЛЮЧЕНИЕ}
  1409.             }
  1410.        
  1411.         В ходе работы над курсовым проектом было изучено поведение MySQL при обработке SQL запросов. Протестирована механика многих оптимизаций, которые могут быть применены ко входной базе данных. Большинство этих оптимизаций было осуществлено. Были оценены улучшившиеся показатели работы, были произведены тесты производительности полученной системы.\\
  1412.        
  1413.         {
  1414.                 \subsection{Возможные дальнейшие улучшения}
  1415.             }
  1416.  
  1417.             Данная работы не может быть названа полностью законченной, так как абсолютно любую систему можно так или иначе улучшить. Вопрос состоит лишь в том, насколько необходимо ``улучшение'', и не приведет ли оно к противоположному результату в другом месте. Так, оптимизация конкретной базы данных в рамках работы шла ``с оглядкой'' на выполнение конкретного моделируемого запроса. \\
  1418.            
  1419.             При возникновении подобной необходимости в будущем, работа может быть продолжена и доведена до необходимиого состояния с учетом будущих потрбностей и возможностей.
  1420.  
  1421.  
  1422.   \newpage
  1423.     <bibtexсписок литературы>
  1424.  
  1425.    \end{document}
Advertisement
Add Comment
Please, Sign In to add comment