BC/NW 2021№ 2 (38):11.1

РАЗРАБОТКА УТИЛИТЫ ДЛЯ ПРЕОБРАЗОВАНИЯ SQL ЗАПРОСОВ И ВИЗУАЛИЗАЦИИ ИСХОДНОЙ СХЕМЫ БАЗЫ ДАННЫХ

Абросимов Л.И., Пичугин Д.А

 

С каждым годом увеличивается объем генерируемой человеком информации. К 2020 году объем хранимых данных увеличился примерно до 40 зеттабайт (1 ЗБ ~ 1 миллиард ГБ). К 2025 году прогнозируется увеличение до 400 зеттабайт. Соответственно, управление структурированными и неструктурированными данными при помощи современных технологий — сфера, которая становится все более важной. Интересуются большими данными как отдельные компании, так и целые государства.

Аналитика данных позволяет выявлять крайне ценную информацию из наборов данных. Благодаря этому бизнес, например, может определять тенденции, прогнозировать производственные показатели и оптимизировать собственные расходы.

Однако перед тем, как данные станут пригодны для анализа, они должны пройти процесс подготовки. Под подготовкой данных подразумевается их сбор, структурирование, очистка от избыточной информации, выгрузка в аналитические системы и многое другое.

При формировании новых наборов данных, либо оптимизации существующих возникает задача анализа взаимосвязей объектов базы данных или хранилища. В случае, когда нет визуального представления этой базы данных, а информацию о таблицах и их отношениях можно получить лишь из SQL запросов, задача анализа сильно усложняется. В таком случае возникает потребность в инструменте, помогающем получить наглядное представление базы данных.

В рамках данного проекта осуществлена разработка программы, генерирующей графическую модель из SQL DDL скриптов, и ее интерфейса.

 

Анализ предметной области и постановка задачи.

Предметная область

Компания «Кадровый учет» входит в концерн «Объединение» и занимается кадровым администрированием всего концерна. Для хранения информации о сотрудниках применяется СУБД Oracle Database. Одной из задач компании является подготовка срезов данных по базе данных сотрудников для их предоставления в подразделения концерна, занимающиеся бизнес-аналитикой.

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

Анализ структуры базы данных – довольно трудоемкий процесс. Для упрощения этой задачи, данные необходимо визуализировать. Визуализация данных — это представление данных в виде, который обеспечивает наиболее эффективную работу человека по их изучению. Наиболее распространенный и информативный вид визуализации данных – это графики и диаграммы.

В данном случае, необходим инструмент, позволяющий получить графическую модель базы данных, используя SQL скрипты для создания таблиц, а также их связей (DDL).

 Анализ предметной области

Визуальное представление базы данных должно быть достаточно информативным и не содержать избыточной информации. Оптимальный вид графической модели – при котором она соответствует этим критериям, легка для восприятия и анализа (рис. 1.).

Графическая нотация схемы представлена на рис. 1:

Рис. 1 Пример графической модели базы данных

·       Совокупность таблиц и их отношения представлена в виде ориентированного графа.

·       В вершинах графа находятся таблицы, ребрами обозначаются зависимости таблиц.

·       В каждой вершине указано название схемы, в которой содержится таблица, название самой таблицы, названия и типы столбцов.

·       Направление рёбер обозначает следующее: таблица, лежащая в вершине, от которой исходит ребро, зависит от таблицы, лежащей в вершине, в которую оно приходит. Например, таблица table1 зависит от таблицы table2, таблицы table1, table2, table3 зависят от таблицы table 4.

Стоит отметить, что такая схема напоминает визуальное представление базы данных иерархического типа [2]. Однако в таком виде может быть представлена любая база данных.

Анализ существующих решений

Из множества инструментов для работы с Oracle DB в первую очередь стоит отметить Oracle SQL Developer [3]. Это интегрированная среда разработки на языках SQL и PL/SQL, с возможностью администрирования баз данных, ориентирована на применение в среде Oracle Database. Продукт предоставляется корпорацией Oracle бесплатно.

Oracle SQL Developer включает в себя инструмент для моделирования базы данных, позволяющий задавать таблицы с помощью SQL DDL скриптов. Однако, визуальные модели, полученные при помощи данного инструмента, имеют ряд недостатков. Модель, построенная этим инструментом приведена на рис. 2.

Из рис. 2 мы видим, что на визуальной схеме присутствуют пересечения стрелок, отображающих связи таблиц. Также видно, что скрипты типа CREATE TABLE AS SELECT не поддерживаются. Таблица, для построения которой использован скрипт данного типа находится в правом верхнем углу рисунка. Названия столбцов отображены не корректно, типы данных не распознаны.

Когда речь идет о выборке данных в виде отдельной таблицы, построение таблиц по типу CREATE TABLE AS SELECT является наиболее удобным способом. Поэтому поддержка данного типа скриптов – важный критерий при выборе инструмента для моделирования базы данных.

Рис. 2 Пример модели, построенной в Oracle SQL Developer

Также для решения подобных задач существует множество аналогичных инструментов для разработки и администрирования баз данных [4]. Наиболее популярные из них это Dataedo, SchemaSpy, DataGrip, Navicat. Данные продукты позволяют построить диаграмму базы данных, основываясь на её метаданных.

Dataedo, SchemaSpy и DataGrip имеют существенный недостаток: в них нет возможности добавления на визуальную модель базы данных еще не существующих в базе данных таблиц [5-7]. При отсутствии у разработчика доступа к метаданным, данные пакеты не подойдут для решения задачи визуализации.

Построение визуальных моделей из SQL запросов реализовано в Navicat [8].

Среди плюсов Navicat стоит также выделить возможность одновременного использования до 7 различных источников данных [9]. Он поддерживает наиболее популярные СУБД: MySQL, MariaDB, MongoDB, SQL Server, Oracle, PostgreSQL, и SQLite. Также данный пакет совместим с облачными хранилищами, такими как Amazon RDS, Amazon Aurora, Amazon Redshift, Microsoft Azure, Oracle Cloud, Google Cloud и MongoDB Atlas [8]. Однако так как данный продукт помимо построения диаграммы имеет множество других возможностей, приобретение его лишь для визуализации для некоторых компаний может быть нерациональным решением в силу стоимости Navicat, которая на данный момент составляет свыше 1200 долларов для полной коммерческой лицензии данного продукта.

Постановка задачи

Основными задачами являются:

·       разработка алгоритма и программы для построения визуальной схемы базы данных из SQL запросов.

·       разработка удобного интерфейса для управления программой.

Разрабатываемое приложение должно удовлетворять следующим требованиям:

·       должен быть предусмотрен пользовательский интерфейс, позволяющий ввести исходные данные;

·       программа должна получать на вход DDL (Data Definition Language) SQL запросы в виде текстовых файлов;

·       программа должна предоставлять пользователю графическую модель на выходе;

·       графическая модель должна быть информативной и не содержать избыточной информации;

·       входные запросы должны полностью соответствовать синтаксису PL/SQL;

·       программа должна быть написана на языке Java.

 

Разработка алгоритма и программы, реализующей приложение

Выбор средств разработки и обоснование алгоритма

Для построения визуальной модели используется графический пакет GraphViz [9]. Выбор пакета обусловлен его широким функционалом, простотой в использовании, доступностью и наличием подробной документации. Данный графический пакет предназначен для визуализации графов. GraphViz равномерно распределяет вершины и ребра графа, а также сводит к минимуму количество пересечений ребер, что позволяет получить легко читаемую визуализацию.

В GraphViz используется язык описания графов DOT. На рис. 3 приведен пример графа, описанного с помощью DOT.

graph graphname {

     // label - видимое название вершины

     a [label="Foo"];

     // shape - определение формы вершины

     b [shape=box];

     // color - определение цвета ребра

     a -- b -- c [color=blue];

     // style - определение стиля ребра

     b -- d [style=dotted];

}

Рис. 3 Пример графа, описанного с помощью DOT

На рис. 4 представлен результат обработки данной конструкции пакетом GraphViz.

Для представления базы данных в виде графа на языке DOT, информацию о таблицах из SQL DDL запросов необходимо извлечь и структурировать.

Преобразование SQL запросов происходит в два этапа. Сначала проводится синтаксический анализ и представление запросов как дерева объектов. Затем на его основе происходит визуализация и представление в виде графической модели.

220px-DotLanguageAttributes

Рис. 4 Пример графа, построенного в GraphViz

Согласно техническому заданию, программа должна быть разработана на языке высокого уровня Java на платформе Java SE. В качестве редактора кода выбрана среда разработки IntelliJ IDEA [10]. В данной среде разработки обеспечена полная поддержка Java SE, а также интеграция с наиболее популярными системами контроля версий, что облегчает разработку и отладку приложения. Для автоматизации сборки проекта используется фреймворк Maven.

Большая часть необходимого функционала для синтаксического анализа реализована в фреймворке JSqlParser. Данный фреймворк позволяет в результате синтаксического анализа скриптов SQL, представить их в виде дерева объектов (частей запроса, таблиц, столбцов и т.д.) [11]. Однако данный фреймворк не поддерживает запросы типа CREATE AS SELECT, а также не распознает зависимости таблиц. За основу синтаксического анализа в программе взят данный фреймворк, а также реализован весь недостающий функционал.

Фреймворк JSqlParser поддерживает следующий синтаксис для запросов CREATE TABLE (рис. 5).

CREATE TABLE table_name

 (

 column1 datatype [ property1, property2, … propertyn ],

 column2 datatype [ property1, property2, … propertyn ],

 …

 column_n datatype [ property1, property2, … propertyn ],

 );

Рис. 5 Синтаксис запросов CREATE TABLE, поддерживаемых JSqlParser

Под [ property1, property2, … propertyn ] подразумевается список свойств столбца.

Одним из существенных недостатков данного ограничения синтаксиса – неспособность распознавания внешних ключей таблицы (при добавлении в запрос строки CONSTRAINT constraint_name FOREIGN KEY(column1) REFERENCES table_name(column2), JSqlParser решит, что это столбец с названием CONSTRAINT, типов данных constraint_name и свойствами FOREIGN KEY(column1) REFERENCES table_name(column2)). Поскольку для данного типа запросов связи таблиц определяются по внешним ключам, в программе должна присутствовать поддержка внешних ключей. Данный функционал доработан в программе, добавлена поддержка CONSTRAINT ограничений, в частности FOREIGN KEY, PRIMARY KEY. Поддерживаемый программой синтаксис CREATE TABLE запросов:

CREATE TABLE table_name

 (

 column1 datatype [ property1, property2, … propertyn ],

 column2 datatype [ property1, property2, … propertyn ],

 …

 column_n datatype [ property1, property2, … propertyn ],

[CONSTRAINT constraint_name FOREIGN KEY(column1) REFERENCES table_name(column2)

| CONSTRAINT constraint_name PRIMARY KEY (column1, column2, … column_n)]

 );

Рис. 6 Поддерживаемый программой синтаксис CREATE TABLE

Как сказано ранее, JSqlParser не поддерживает запросы типа CREATE TABLE AS SELECT. Однако фреймворк распознает стандартные SQL DML запросы типа SELECT. За основу преобразования CREATE TABLE AS SELECT запросов в программе взят функционал синтаксического анализа SELECT запросов с помощью JsqlParser.

Поддерживаемый программой синтаксис CREATE TABLE AS SELECT запросов:

CREATE TABLE new_table  AS

          (SELECT old_table_1.column_1 as new_column_1,

          old_table_2.column_2 as new_column_2,

          old_table_n.column_n as new_column_n,

FROM old_table_1

[ LEFT | RIGHT | INNER | FULL ] JOIN old_table_2 ON {JOIN_CLAUSE}

[ LEFT | RIGHT | INNER | FULL ] JOIN old_table_n ON {JOIN_CLAUSE}

WHERE {WHERE_CLAUSE}

ORDER BY {ORDER_BY_CLAUSE}

);

Рис. 7 Поддерживаемый программой синтаксис CREATE TABLE AS SELECT

В CTAS запросах не поддерживаются агрегатные функции, а также предложения GROUP BY и HAVING.

При разработке использованы основные концепции объектно-ориентированного программирования [10].

Основные классы и их назначение

SimpleGuiреализует графический интерфейс пользователя.

DiagramBuilderинициализирует модуль логирования, запускает синтаксический анализ и построение графической схемы.

Parser – выполняет синтаксический анализ, преобразует входные запросы в объекты.

Session – хранит массив таблиц для текущего процесса синтаксического анализа.

MyTable – экземпляры класса – преобразованные таблицы.

Formatter – отвечает за построение визуальной модели.

Диаграмма классов представлена на рис. 8. Она демонстрирует общую структуру иерархии классов, их столбцов, методов и взаимодействий между классами. Классы программы объединены в общий пакет vizualize. Класс SimpleGui находится во вложенном пакете visualize.gui, а классы Parser, DiagramBuilder, Session, MyTable, Formatter находятся в visualize.builder. Разделение классов по пакетам обеспечивает организацию классов в пространстве имен [12]. Классы пакета visualize.builder отвечают за программно-аппаратную часть приложения, а классы содержащиеся в visualize.gui отвечают за клиентскую сторону пользовательского интерфейса приложения.

Описание полей и методов классов

SimpleGui

Данный класс наследуется от JFrame. Класс JFrame включен в библиотеку Swing. Swing является частью Java Development Kit, реализует набор графических компонентов и предназначена для создания пользовательских интерфейсов. Класс JFrame реализует функционал построения окна с рамкой.

Рис. 8 Диаграмма классов программы

Модификатор доступа класса - public.

Таблица 1 – Поля класса SimpleGui

Название

Модификатор доступа

Тип

Описание

mainPanel

private

JPannel

Главное окно интерфейса

addFiles

private

JButton

Кнопка добавления файлов

runner

private

JButton

Кнопка запуска основной программы

textArea

private

JTextArea

Текстовое поле, в котором отображаются пути к входным файлам

fileChooser

private

JFileChooser

Диалоговое окно выбора файлов

paths

private

ArrayList<String>

Массив, хранящий пути к входных данным

 

Таблица 2 – Методы класса SimpleGui

Название

Модификатор доступа

Входные параметры

Тип

Описание

SimpleGui

private

String title

-

Конструктор. Обработка действий пользователя

main

public

-

void

Начальная точка программы

$$$setupUI$$$

private

-

void

Конфигурация свойств компонентов интерфейса

$$$getRootComponent$$$

private

-

void

Системный метод

 

Методы $$$setupUI$$$ и $$$getRootComponent$$$ сгенерированы автоматически редактором пользовательского интерфейса Intellij IDEA GUI Designer и являются обязательными для корректной работы интерфейса. Данный редактор является одной из базовых компонент используемой среды разработки.

DiagramBuilder

Модификатор доступа класса – public.

Таблица 3 – Поля класса DiagramBuilder

Название

Модификатор доступа

Тип

Описание

logger

private

Logger

Модуль логирования программы

 

Таблица 4 – Методы класса DiagramBuilder

Название

Модификатор доступа

Входные параметры

Тип

Описание

DiagramBuilder

private

-

-

Конструктор. Инициализирует модуль логирования.

build

public

String[] args

File

Запускает синтаксический анализ и построение схемы. Возвращает файл с графической схемой

 

Parser

Метод использует классы фреймворка JSqlParser. JSqlParser предоставляет базовый функционал синтаксического анализа SQL запросов с возможностью преобразования в запросов в дерево объектов. Наиболее используемые в программе классы JSqlParser приведенны в таблице 5.

Таблица 5 – Классы JSqlParser

Пакет

Класс

Описание

net.sf.jsqlparser.parser

CCJSqlParserManager

Использует JavaCC, проверяет текст и создает объекты.

net.sf.jsqlparser.schema

Table

Таблица. Содержит имя таблицы и схемы.

net.sf.jsqlparser.statement

Statement

SQL запрос

net.sf.jsqlparser.statement.create.table

CreateTable

SQL запрос Create Table

net.sf.jsqlparser.statement.create.table

ColumnDefinition

Столбец таблицы из SQL запроса. Содержит название, тип и свойства

net.sf.jsqlparser.statement.select

Select

SQL запрос Select

net.sf.jsqlparser.statement.select

PlainSelect

SQL Select, разбитый по группам операций

 

Модификатор доступа класса – package private.

Таблица 6 – Поля класса Parser

Название

Модификатор доступа

Тип

Описание

logger

private

Logger

Модуль логирования программы

session

private

Session

Объект типа session для текущего запуска

createAsSelectPaths

private

ArrayList<String>

Массив путей к CTAS скриптам

Таблица 7 – Методы класса Parser

Название

Модификатор доступа

Входные параметры

Тип

Описание

Parser

package-private

-

-

Конструктор. Инициализирует модуль логирования

parse

package-private

String[] args

void

Преобразует входной массив в объекты Statement

parse

private

Statement statement

void

Преобразует объект Statement в MyTable, добавляет его в session

checkExistence

private

Table table

boolean

Проверяет существование таблицы в текущем session

checkCAS

private

String path

boolean

Проверяет, является ли скрипт SQL CTAS

parseCAS

private

-

void

Преобразует SQL CTAS скрипты в объекты MyTable, добавляет в session

parseColumns

private

List<String> relations, List selectItems

ArrayList< ColumnDefinition>

Анализирует SQL скрипт, возвращает столбцы таблицы

getColumnDatatype

private

List<String> relations, String column

ColDataType

Выполняет поиск столбца в проанализированных таблицах, возвращает его тип.

findTable

private

List<String> relations, String colTableName

MyTable

Ищет таблицу из relations, имеющую столбец colTableName

findTable

private

String tableName

MyTable

Ищет таблицу в session по названию

findCol

private

MyTable table, String searchedColumn

ColDataType

Ищет столбец в таблице по названию, возвращает его тип

checkForeignKeys

private

CreateTable createTable

List<String>

Проверяет наличие внешних ключей в скрипте, возвращает список зависимостей

checkForeignKey

private

Object col

String

Проверяет наличие внешних ключей в скрипте. Возвращает одну зависимость

getRelatedTable

private

String columnDefinition

String

Извлекает из строки название зависимой таблицы

getSession

package-private

-

Session

Getter для session.

 

Session

Модификатор доступа класса – package private.

Таблица 8 – Поля класса Session

Название

Модификатор доступа

Тип

Описание

tables

private

ArrayList<MyTable>

Объекты-таблицы текущего запуска

 

Таблица 9 – Методы класса Session

Название

Модификатор доступа

Входные параметры

Тип

Описание

setTable

package-private

CreateTable createTable

void

Добавляет элемент в tables

setTable

package-private

CreateTable createTable, List relations

void

Добавляет элемент в tables

setTable

package-private

MyTable myTable

void

Добавляет элемент в tables

getTables

package-private

-

MyTable

Getter для tables

MyTable

Модификатор доступа – package private.

Таблица 10 – Поля класса Session

Название

Модификатор доступа

Тип

Описание

table

private

Table

Объект, хранящий имя таблицы и схемы

columns

private

ArrayList<ColumnDefinition>

Множество столбцов таблицы, их типов и свойств

relations

private

List<String>

Список связей таблицы

 

Таблица 11Методы класса Session

Название

Модификатор доступа

Входные параметры

Тип

Описание

setTable

package-private

Table table

void

Setter для tables

setColumns

package-private

ArrayList<ColumnDefinition> columns

void

Setter для columns

setRelations

package-private

List relations

void

Setter для relations

getTables

package-private

-

Table

Getter для tables

getColumns

package-private

-

ArrayList<ColumnDefinition>

Getter для columns

getRelations

package-private

-

List<String>

Getter для relations

Formatter

Модификатор доступа – package private.

Таблица 12 – Поля класса Formatter

Название

Модификатор доступа

Тип

Описание

graph

private

StringBuilder

Хранит DOT запрос для создания графа

cfgProp

private

String

Путь к конфигурационному файлу

configFile

private

Properties

Конфигурация

TEMP_DIR

private

String

Путь к директории для временных файлов

DOT

private

String

Путь к dot.exe

dpiSizes

private

int[]

Значения dpi для dot

currentDpiPos

private

int

Параметр Gdpi для dot

logger

private

Logger

Модуль логирования программы

 

Таблица 13 – Методы класса Formatter

Название

Модификатор доступа

Входные параметры

Тип

Описание

Formatter

package-private

-

-

Конструктор. Инициализирует модуль логирования

add

package-private

String line

void

Добавляет строку в graph

addln

package-private

String line

void

Добавляет строку и переход на новую строку в graph

addln

package-private

-

void

Добавляет переход на новую строку в graph

start_graph

package-private

-

String

Добавляет начало dot запроса

end_ graph

package-private

-

String

Добавляет завершение dot запроса

getDotSource

package-private

-

String

Возвращает dot запрос

getGraph

package-private

String dot_source, String type

byte[]

Возвращает граф в виде массива байт

writeDotSourceToFile

private

String str

File

Запись dot запроса в файл

writeGraphToFile

package-private

byte[] img, File to

int

Запись графа в буфер

toDotFormat

package-private

Session thisSession

String

Построение dot запроса

tableToDotFormat

private

MyTable table

String

Представление таблицы в виде части dot запроса

relationToDotFormat

private

MyTable table

String

Представление отношения в виде части dot запроса

get_img_stream

private

File dot, String type

byte[]

Выполнение dot запроса

Разработка алгоритма

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

Сначала происходит ввод исходных данных пользователем, затем данные последовательно считываются и обрабатываются, идет синтаксический анализ. Преобразованные на основе синтаксического анализа данные используются для построения графа. Алгоритмы синтаксического анализа и преобразования данных, а также построения графической модели описаны ниже.

Общий алгоритм графически представлен на рис. 9.

Рис. 9 Общий алгоритм программы

Процессы, обозначенные на рис. 9 как «Обработка стандартного DDL» и «Обработка скриптов DDL CTAS» представлены на рис. 10 и 11 соответственно.

Рис. 10 Алгоритм обработки стандартного DDL

Рис. 11 Алгоритм обработки скриптов DDL CTAS

 

Алгоритм синтаксического анализа

Функции синтаксического анализа:

·       обработка массива SQL DDL запросов;

·       извлечение из запросов информации о таблицах;

·        представление этой информации в виде структуры объектов

Входные данные: массив объектов типа String. Каждый элемент хранит путь к текстовому файлу.

Выходные данные: объект типа Session, хранящий полученные из запросов таблицы, их связи и столбцы.

В основе синтаксического анализа в данной программе заложен метод сопоставления с образцом [13]. В качестве шаблона используются регулярные выражения.

Массив входных данных обрабатывается последовательно. Из массива извлекается очередная строка, считывается файл, лежащий по адресу из данной строки. Проверяется соответствие файла одному из поддерживаемых форматов SQL DDL запросов.

 Сначала проверяется, соответствуют ли скрипт типу CREATE TABLE AS SELECT. Для этого используется регулярное выражение ^CREATE TABLE .*(AS)?SELECT. Поскольку запросы CTAS строятся на других таблицах, они преобразуются в последнюю очередь. Поэтому если при проверке найден такой скрипт, файл с этим скриптом копируется в буферную директорию, а путь к нему заносится в буферный массив. Если файл не относится к типу CREATE TABLE AS SELECT, выполняется дальнейшая проверка.

Проверяется соответствие типу CREATE TABLE. Для этого скрипт средствами JSqlParser преобразуется в объект типа Statement. Statement – класс, соответствующий SQL выражению, поддерживаемому фреймворком JSqlParser. От него наследуются классы, соответствующие конкретным типам запросов (CreateTable, Select, Truncate, Insert, Update и др.). Далее определяется, принадлежит ли созданный объект классу CreateTable с учетом наследования. При положительном результате проверки, выполняются дальнейшие преобразования. Если одно из преобразований не удалось, это означает, что проверяемый файл не удовлетворяет требованиям (не является SQL DDL скриптом, либо не соответствует поддерживаемому синтаксису).

Для таблицы создается объект типа MyTable. Из объекта типа CreateTable извлекаются имя таблицы и схемы, названия и типы столбцов при помощи методов JSqlParser. Выполняется поиск внешних ключей по регулярным выражениям (?<=FOREIGN KEY [(]).* и (?<=REFERENCES ).*(?= ). Полученной о таблице информацией заполняется объект типа MyTable. Массив всех объектов MyTable, созданных для текущего выполнения программы хранится в объекте типа Session.

После обработки входного массива выполняется проверка на пустоту буферного массива, который заполнялся ранее при обнаружении скриптов CREATE TABLE AS SELECT. Если массив не пуст, скрипты CTAS считываются и обрабатываются.

Для анализа и преобразования скрипта средствами JSqlParser скрипт типа CTAS форматируется. Название таблицы сохраняется в буферную переменную. Сам скрипт обрезается, оставляется только SELECT часть запроса, поскольку SELECT запросы легко преобразуются с помощью JSqlParser. Создается объект типа Select, в результате проверки его полей извлекаются названия столбцов, а также FROM и JOIN части запроса для определения связей таблицы. Выполняется поиск таблиц из FROM и JOIN предложений (это таблицы, на основе которых строится текущая). Если они найдены, запоминаются связи с ними. Типы данных для столбцов таблицы берутся из таблиц, на основе которых она построена. Для этого находится объект с таблицей, в нем находится столбец с соответствующим названием и извлекается его тип. Полученной о таблице информацией заполняется объект типа MyTable.

Стоит выделить проблему, возникающую при анализе CREATE TABLE AS SELECT скриптов. Таблицы, создаваемые с помощью CTAS, строятся на основе других таблиц, из которых делается выборка данных. При этом они могут строиться на основе таблиц, которые также создаются с помощью CTAS. Может возникнуть ситуация, когда скрипт для создания таблицы-родителя еще не обработан, а ее потомок обрабатывается в данный момент. Для такой таблицы будет невозможно обнаружить связи и типы данных столбцов.

Для решения этой проблемы цикл обработки скриптов CREATE TABLE AS SELECT организован особым образом. Скрипты заносятся в очередь. Скрипт извлекается из очереди, выполняется поиск таблиц-родителей. Если хотя бы одна таблица-родитель не обработана ранее (для нее не найден объект MyTable в Session), то скрипт снова заносится в конец очереди и будет проверен после всех остальных. В худшем случае при однократной проверке N скриптов типа CTAS должен быть обработан минимум 1 скрипт, для этого будет сделано N итераций. На втором кругу проверки в худшем случае будет обработан также 1 скрипт, при этом число итераций уменьшится до N-1. Таким образом, общее максимальное число итераций для успешного анализа всех N скриптов типа CTAS находится как N-я частичная сумма ряда натуральных чисел. Обозначим общее число итераций I. Для него справедлива следующая формула:

Цикл обработки CTAS скриптов проходит с учетом максимального числа итераций для текущей длины массива скриптов. Если лимит итераций достигнут, оставшиеся в очереди скрипты преобразуются, при этом их связи и типы данных столбцов (все, если не найдена ни одна таблица-родитель, либо только их часть) не будут определены. Неизвестные типы данных для таких таблиц будут иметь на визуальной схеме значение NULL.

Алгоритм построения графической модели

Входные данные: массив объектов, хранящих информацию о таблицах.

Выходные данные: визуальная схема базы данных.

Построение графической модели выполняется в GraphViz. Для этого сначала должен быть сформирован dot запрос для создания графа.

Информация о таблицах извлекается из MySession объектов, на ее основе собирается dot запрос.

Пример формируемого запроса:

digraph G {

table1 [style=filled, fillcolor="#BFC9CA", shape=none, margin=0, label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4"><TR><TD COLSPAN="2">SCHEMA_NAME.TABLE_NAME</TD></TR><TR><TD>COLUMN_1</TD><TD>COLUMN_DATATYPE_1</TD></TR><TR><TD>COLUMN_2</TD><TD>COLUMN_DATATYPE_2</TD></TR></TABLE>>];

table1 [style=filled, fillcolor="#BFC9CA", shape=none, margin=0, label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4"><TR><TD COLSPAN="2">SCHEMA_NAME.TABLE_NAME</TD></TR><TR><TD>COLUMN_1</TD><TD>COLUMN_DATATYPE_1</TD></TR><TR><TD>COLUMN_2</TD><TD>COLUMN_DATATYPE_2</TD></TR></TABLE>>];

table1->table2;}

Рис. 12 – Пример DOT запроса, формируемого в программе

На примере показан запрос для создания графической модели базы данных, состоящей из двух таблиц, имеющих между собой связь.

Как можно заметить из примера, стиль узлов dot графа может задаваться html кодом.

Как только запрос будет готов, он сохраняется в файл с расширением .dot. Затем консольной командой вызывается GraphViz и выполняется построение графа. Построенная графическая модель сохраняется в виде PDF файла. Файл открывается сразу после успешного завершения построения. PDF формат позволяет не терять качество схемы при ее масштабировании.

Разработка пользовательского интерфейса

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

Интерфейс содержит текстовое поле и две кнопки, отвечающие за открытие диалогового окна для выбора входных файлов и запуска построения схемы соответственно. Текстовое поле отображает входные данные – пути к файлам, содержащим SQL DDL скрипты.

В результате выполнения основного кода программы откроется построенная графическая модель, либо возникнет всплывающее окно с сообщением об ошибке, возникшей в ходе программы.

Инструкция по использованию интерфейса

Когда пользователь запускает приложение, он видит перед собой интерфейс, представленный на рис. 13.

Рис. 13 Пользовательский интерфейс при его запуске

Сперва он должен ввести данные. Для этого необходимо нажать на кнопку «Select files». По нажатию кнопки открывается диалоговое окно для ввода файлов (рис. 14). В данном окне пользователь нажатием левой кнопки мыши (ЛКМ) должен выбрать файлы со скриптами, которые он хочет преобразовать, затем нажать «Open», либо «Cancel» для отмены ввода. При однократном вызове окна ввода, пользователь может добавить несколько файлов. Для этого выбирать их нужно при зажатой клавише Ctrl, либо, если файлы находятся в одной директории и лежат один за другим, при зажатой клавише Shift нажатием ЛКМ на первый в списке файл, затем на последний. В каждом файле должен лежать лишь 1 SQL DDL скрипт. Ввод файлов, содержащих более 1 скрипта, либо не соответствующих синтаксису SQL DDL приведет к ошибке в ходе работы программы.

Рис. 14 Диалоговое окно выбора входных файлов

Все выбранные пользователем файлы будут отображаться в главном окне интерфейса (рис. 15). Убедившись, что все необходимые файлы с данными добавлены, пользователь может запустить построение графической модели, нажав на кнопку «Maker diagram». Построенная схема откроется в виде PDF документа.

Рис. 15 Интерфейс после ввода данных

 

Рис. 16 Пример всплывающего окна при ошибке выполнения программы

Тестирование

Тестирование проводится по стратегии «черного ящика» - анализируется результат выполнения программы в зависимости от входных данных. Целью проведения тестирования приложения является демонстрация корректной работы при входных данных, удовлетворяющих требованиям, а также данных, не соответствующих им.

Ввод входных данных осуществляется пользователем через интерфейс приложения.

Порядок ввода данных следующий:

1.    Пользователь открывает программу, перед ним открывается главное окно программы (рис. 13)

2.    Необходимо нажать на кнопку «Select files»

3.    В открывшемся диалоговом окне (рис. 14) открыть директорию, содержащую подготовленные DDL скрипты. Выбрать один или несколько (с зажатой shift или ctrl) файлов, затем нажать open.

4.    Добавленные файлы будут отображаться в главном окне (рис. 15). Если добавлены не все необходимые файлы со скриптами, повторить действия 2-3.

5.    Как только ввод окончен, нажать на копку «Make diagram». Запустится процесс обработки скриптов. Как только скрипты будут обработаны и визуальная модель построится, на ПК пользователя появится PDF файл diagram.pdf, этот файл откроется сразу по окончанию его создания.

Успешное выполнение программы

В качестве источника входных данных используется база данных компании «Кадровый учет».

Описания таблиц:

·       REGIONS - содержит строки, которые представляют регион, такой как Америка, Азия и так далее.

·       COUNTRIES - содержит строки для стран, каждая из которых связывается с областью.

·       LOCATIONS -  содержит определенный адрес определенного офиса, склада или производственного участка компании в определенной стране.

·       DEPARTMENTS - показывает подробные данные об отделах, в которых работают сотрудники. У каждого отдела может быть отношение, представляющее начальника отдела в таблице EMPLOYEES.

·       EMPLOYEES - содержит подробные данные о каждом сотруднике, работающем в отделе. Некоторые сотрудники могут быть не присвоены никакому отделу.

·       JOB - содержит должности, которые могут быть закреплены за каждым сотрудником.

·       JOB_HISTORY - содержит историю изменения должностей сотрудников. Если сотрудник изменяет отдел в пределах задания или меняет работу в пределах отдела, новая строка вставляется в эту таблицу с более ранней информацией о задании сотрудника.

База данных хранится в Oracle Database. Для того, чтобы ввести информацию о ней в программу, подготовлен набор DDL скриптов, описывающих создание таблиц, а также их связи. Информация о каждой таблице и ее связях представлена в отдельном скрипте с расширением .ddl. Ниже приведен листинг данных скриптов.

 

/* Регионы */

CREATE TABLE regions (

          /* Уникальный идентификатор региона */

    region_id     NUMBER(10) NOT NULL,

          /* Название региона */

    region_name   VARCHAR2(50)

);

 

/* Определение первичного ключа таблицы */

ALTER TABLE regions ADD CONSTRAINT regions_pk PRIMARY KEY ( region_id );

 

Рис. 17 Regions.ddl

 

/* Страны */

CREATE TABLE countries (

          /* Уникальный идентификатор страны */

    country_id     NUMBER(10) NOT NULL,

          /* Название страны */

    country_name   VARCHAR2(30),

          /* Идентификатор региона, к которому относится страна */

    region_id      NUMBER(10) NOT NULL

);

 

/* Определение первичного ключа таблицы */

ALTER TABLE countries ADD CONSTRAINT countries_pk PRIMARY KEY ( country_id );

 

/* Определение внешнего ключа таблицы к таблице регионов*/

ALTER TABLE countries

    ADD CONSTRAINT countries_regions_fk FOREIGN KEY ( region_id )

        REFERENCES regions ( region_id )

    NOT DEFERRABLE;

 

 

Рис. 18 Countries.ddl

 

/* Адрес */

CREATE TABLE locations (

          /* Уникальный идентификатор адреса */

    location_id      NUMBER(10) NOT NULL,

          /* Улица */

    street_address   VARCHAR2(100),

          /* Индекс */

    postal_code      NUMBER(10),

          /* Город */

    city             VARCHAR2(100),

          /* Регион страны */

    state_province   VARCHAR2(100),

          /* Идентификатор страны */

    country_id       NUMBER(10) NOT NULL

);

 

/* Определение первичного ключа таблицы */

ALTER TABLE locations ADD CONSTRAINT locations_pk PRIMARY KEY ( location_id );

 

/* Определение внешнего ключа таблицы к таблице стран */

ALTER TABLE locations

    ADD CONSTRAINT locations_countries_fk FOREIGN KEY ( country_id )

        REFERENCES countries ( country_id )

    NOT DEFERRABLE;

 

 

Рис. 19 Locations.ddl

 

/* Подразделения */

CREATE TABLE departments (

          /* Уникальный идентификатор подразделения */

    department_id     NUMBER(10) NOT NULL,

          /* Название подразделения */

    department_name   VARCHAR2(100),

          /* Руководитель подразделения */

    manager_id        NUMBER(10),

          /* Адрес подразделения */

    location_id       NUMBER(10) NOT NULL

);

 

/* Определение первичного ключа таблицы */

ALTER TABLE departments ADD CONSTRAINT departmens_pk PRIMARY KEY ( department_id );

 

/* Определение внешнего ключа таблицы к таблице адресов */

ALTER TABLE departments

    ADD CONSTRAINT departmens_locations_fk FOREIGN KEY ( location_id )

        REFERENCES locations ( location_id )

    NOT DEFERRABLE;

 

 

Рис. 20 Departments.ddl

 

/* Сотрудники */

CREATE TABLE employees (

          /* Уникальный идентификатор сотрудника */

    employee_id     NUMBER(10) NOT NULL,

          /* Имя сотрудника */

    first_name      VARCHAR2(50),

          /* Фамилия идентификатор сотрудника */

    last_name       VARCHAR2(50),

          /* Электронная почта сотрудника */

    email           VARCHAR2(50),

          /* Мобильный телефон сотрудника */

    phone_number    VARCHAR2(20),

          /* Дата найма сотрудника */

    hire_date       DATE NOT NULL,

          /* Идентификатор должности сотрудника */

    job_id          NUMBER(10) NOT NULL,

          /* Зарплата сотрудника */

    salary          VARCHAR2(30),

          /* Ставка сотрудника */

    comission_pct   VARCHAR2(30),

          /* Идентификатор руководителя сотрудника */

    manager_id      NUMBER(10),

          /* Идентификатор подразделения сотрудника */

    department_id   NUMBER(10) NOT NULL

);

 

/* Определение первичного ключа таблицы */

ALTER TABLE employees ADD CONSTRAINT employees_pk PRIMARY KEY ( employee_id );

 

/* Определение внешнего ключа таблицы к таблице подразделений */

ALTER TABLE employees

    ADD CONSTRAINT employees_departmens_fk FOREIGN KEY ( department_id )

        REFERENCES departments ( department_id )

    NOT DEFERRABLE;

 

/* Определение внешнего ключа таблицы к таблице истории изменения должностей */

ALTER TABLE employees

    ADD CONSTRAINT employees_job_history_fk FOREIGN KEY ( employee_id,

                                                          hire_date )

        REFERENCES job_history ( employee_id,

                                 start_date )

    NOT DEFERRABLE;

 

/* Определение внешнего ключа таблицы к таблице должностей */

ALTER TABLE employees

    ADD CONSTRAINT employees_jobs_fk FOREIGN KEY ( job_id )

        REFERENCES jobs ( job_id )

    NOT DEFERRABLE;

 

Рис. 21 Employees.ddl

 

/* Должности */

CREATE TABLE jobs (

          /* Уникальный идентификатор должности */

    job_id       NUMBER(10) NOT NULL,

          /* Название должности */

    job_title    VARCHAR2(100),

          /* Минимальная зарплата */

    min_salary   VARCHAR2(30),

          /* Максимальная зарплата */

    max_salary   VARCHAR2(30)

);

 

/* Определение первичного ключа таблицы */

ALTER TABLE jobs ADD CONSTRAINT jobs_pk PRIMARY KEY ( job_id );

 

 

Рис. 22 Job.ddl

 

/* История изменения должностей */

CREATE TABLE job_history (

          /* Уникальный идентификатор сотрудника */

    employee_id     NUMBER(10) NOT NULL,

          /* Дата начала работы на должности */

    start_date      DATE NOT NULL,

          /* Дата окончания работы на должности */

    end_date        DATE,

          /* Уникальный идентификатор должности */

    job_id          NUMBER(10) NOT NULL,

          /* Уникальный идентификатор подразделения */

    department_id   NUMBER(10) NOT NULL

);

 

/* Определение первичного ключа таблицы */

ALTER TABLE job_history ADD CONSTRAINT job_history_pk PRIMARY KEY ( employee_id,

                                                                    start_date );

/* Определение внешнего ключа таблицы к таблице подразделений */

ALTER TABLE job_history

    ADD CONSTRAINT job_history_departmens_fk FOREIGN KEY ( department_id )

        REFERENCES departments ( department_id )

    NOT DEFERRABLE;

 

/* Определение внешнего ключа таблицы к таблице должностей */

ALTER TABLE job_history

    ADD CONSTRAINT job_history_jobs_fk FOREIGN KEY ( job_id )

        REFERENCES jobs ( job_id )

    NOT DEFERRABLE;

 

 

Рис. 23 Job_history.ddl

Также для тестирования разработаны скрипты для создания таблиц – срезов данных. DDL скрипты этих таблиц имеют тип CREATE TABLE AS SELECT.

В тестировании проверяется работа приложения на двух срезах данных. Первый отображает сотрудников, работающих только в главном офиса. Второй отображает регионы, где сотрудники компании имеют зарплату, ниже средне допустимой для их должности.

Описания таблиц:

·       EMPLOYEES_OF_MAIN_OFFICE – показывает сотрудников, работающих в главном офисе.

·       EMPLOYEES_OF_REGION – показывает всех сотрудников и регионы, в которых они работают.

·       EMPLOYEES_UNDER_AVG_SALARY – показывает регионы, в которых сотрудники имеют зарплату ниже средне допустимой для их должности.

/* Сотрудники, работающие в главном офисе */

CREATE TABLE employees_of_main_office AS (

          /* Название подразделения */

          SELECT departments.department_name as department_name,

          /* Имя сотрудника */    

          employees.first_name as first_name,

          /* Фамилия сотрудника */

          employees.last_name as last_name     

          FROM employees join departments on employees.department_id = departments.department_id

          join locations on departments.location_id = locations.location_id       

          /* Адрес главного офиса */    

          WHERE locations.street_address LIKE 'MOSCOW, TVERSKAYA ST., 8/2'

          );

 

 

Рис. 24 Employees_of_main_office.ddl

 

/* Сотрудники и регионы, в которых они работают */

CREATE TABLE employees_of_region AS (

          /* Идентификатор сотрудника */

          SELECT employees.employee_id as employee_id,

          /* Имя сотрудника */    

          employees.first_name as first_name,

          /* Фамилия сотрудника */

          employees.last_name as last_name,

          /* Идентификатор региона */

          regions.region_id as region_id,

          /* Название региона */

          regions.region_name as region_name,

/* Идентификатор должности */

employees.job_id,

/* Зарплата сотрудника */

employees.salary

          FROM employees join departments on employees.department_id = departments.department_id

          join locations on departments.location_id = locations.location_id       

          join countries on locations.country_id=countries.country_id

          join regions on countries.region_id=regions.region_id

          );

 

 

Рис. 25 Employees_of_region.ddl

 

 

/* Регионы, в которых работают сотрудники, зарплата которых ниже средне допустимой на данной должности */

CREATE TABLE employees_under_avg_salary AS (

          /* Название региона */

          SELECT employees_of_region.region_name as region_name,

          employees_of_region.employee_id as employee_id,

/* Имя сотрудника */    

          employees_of_region.first_name as first_name,

          /* Фамилия сотрудника */

          employees_of_region.last_name as last_name,

          /* Идентификатор должности */

          jobs.job_id as job_id,

          /* Название должности */

          jobs.job_title as job_title FROM employees_of_region join jobs on employees_of_region.job_id=jobs.job_id

          WHERE ((job_id.max_salary + job_id.min_salary)/2) > employees_of_region.salary

          );

 

 

Рис. 26 Employees_under_avg_salary.ddl

Ожидаемый результат: Программа успешно обработает массив скриптов и сформирует графическую схему с учетом особенностей языка (типов данных, ключевых слов). Схема имеет легко читаемый вид.

Результат:

Программой сгенерирован файл DIAGRAM.pdf, содержащий построенную визуальную модель (рис. 27). Все таблицы, их атрибуты, а также связи корректны, следовательно, тестирование выполнено успешно.

Данная модель наглядно демонстрирует организацию таблиц и их связей в базе данных и пригодна для дальнейшего анализа базы данных.

Рис. 27 Визуальная модель, полученная в результате теста 1

          На рис. 28 и 29 представлены участки данной диаграммы в увеличенном формате.

Рис. 28 Верхний участок схемы

Рис. 29 Нижний участок схемы

Выполнение программы при ошибке в данных

Неподдерживаемый формат SQL

Для данного теста в предыдущий набор скриптов внесены изменения: вместо CTAS скрипта создания таблицы FILMS_OF_ACTOR имеется SELECT запрос.

          SELECT employees_of_region.region_name as region_name,

          employees_of_region.employee_id as employee_id,

/* Имя сотрудника */    

          employees_of_region.first_name as first_name,

          /* Фамилия сотрудника */

          employees_of_region.last_name as last_name,

          /* Идентификатор должности */

          jobs.job_id as job_id,

          /* Название должности */

          jobs.job_title as job_title FROM employees_of_region join jobs on employees_of_region.job_id=jobs.job_id

          WHERE ((job_id.max_salary + job_id.min_salary)/2) > employees_of_region.salary

          ;

 

Рис. 30 Измененный скрипт

Ожидаемый результат: всплывающее окно с ошибкой.

Результат:

Рис. 31 – Результат выполнения программы при входном Select запросе

Полученный результат показывает, что при распознавании SQL запроса типа SELECT, программа сообщает об ошибке и прекращает свою работу, что соответствует ожидаемому результату.

Формат входных файлов, не соответствующий SQL

Входные данные: файл с данными в формате XML.

Ожидаемый результат: всплывающее окно с ошибкой.

Результат:

Рис. 32 – Результат выполнения программы при некорректном формате входных данных

Полученный результат показывает, что в случае, когда содержимое файла не соответствует SQL DDL, программа не распознает в нем ни один из поддерживаемых форматов и сообщает об ошибке.

Заключение

В результате разработки исследован метод визуализации данных для повышения производительности анализа баз данных и проектирования срезов данных. Были проанализированы инструменты для визуализации данных.

На основе собранного материала было разработано приложение, преобразующее набор SQL DDL скриптов в визуальную модель базы данных. Произведен выбор среды программирования и разработан пользовательский интерфейс, обеспечивающий удобный ввод данных. Разработаны алгоритмы синтаксического анализа скриптов и визуализации полученной в результате синтаксического анализа информации, а также код, реализующий эти алгоритмы в программе. Общий объем исходного кода программы – 1084 строки.

Проведено тестирование программы на массиве SQL скриптов для создания таблиц, которые преобразуются программой в наглядную графическую модель базы данных.

Данное приложение может применяться разработчиками баз данных при необходимости анализа таблиц существующей базы данных с целью ее доработки.

 Список используемой литературы

1.    Брила, Б. Oracle Database 11g. Настольная книга администратора баз данных / Б. Брила, К. Луни. – Litres, 2019. – 848 с.

2.    Левчук, Е. А. Технологии организации хранения и обработки данных: учеб. пособие / Е. А. Левчук. – 3-е изд. – Минск: Выш. шк., 2007. – 239 с.

3.    Oracle SQL Developer: [сайт]. URL: https://docs.oracle.com/en/database/oracle/sql-developer/index.html

4.    DBMS Tools: [сайт]. URL: https://dbmstools.com/categories/database-diagram-tools

5.    Dataedo: [сайт]. URL: https://dataedo.com/product

6.    SchemaSpy: [сайт]. URL: https://schemaspy.readthedocs.io/en/latest/

7.    DataGrip: [сайт]. URL: https://www.jetbrains.com/ru-ru/datagrip/features/

8.    Navicat Premium: [сайт]. URL: https://www.navicat.com/en/products/navicat-premium

9.    GraphViz: [сайт]. URL: https://www.graphviz.org/

10.JSqlParser: [сайт]. URL: http://jsqlparser.sourceforge.net/home.php

11.Давыдов С. В. IntelliJ IDEA. Профессиональное программирование на Java / С. В. Давыдов – БХВ-Петербург, 2005. – 800 с.

12.Ian F. Darwin, Java Cookbook, Fourth Edition. O'Reilly Media, 2019, 614p.

13.Joshua Bloch, Effective Java, 3rd Edition. Addison-Wesley Professional, December 2017, 377p.