Разработка программного средства, позволяющего оптимизировать SQL-скрипты
Изучение и характеристика особенностей пользовательского интерфейса. Исследование архитектуры приложения и основных классов, связанных с парсером. Рассмотрение и анализ сущности teradata database – системы массовой параллельной обработки данных.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | дипломная работа |
Язык | русский |
Дата добавления | 31.10.2016 |
Размер файла | 418,6 K |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
Размещено на http://www.allbest.ru/
Оглавление
Список определений и сокращений
Введение
1. Анализ предметной области
1.1 Архитектура Teradata
1.2 Встроенный оптимизатор в Teradata
1.3 Оптимизация запросов в Teradata
1.4 Обзор аналогов
2. Парсер
3. Оптимизатор
Заключение
Источники
Приложение
Список определений и сокращений
Данный раздел содержит список всех используемых в работе сокращений. Расшифровки и переводы на русский язык англоязычных сокращений представлены в таблице 1. Расшифровки русскоязычных сокращений представлены в таблице 2.
Таблица 1 Расшифровка англоязычных сокращений
Сокращение |
Расшифровка |
Русский перевод |
|
AMP |
Access Module Processor |
Модуль обработки данных |
|
CPU |
Central Processing Unit |
Центральное процессорное устройство |
|
DBMS |
Database Management System |
Система управления базами данных |
|
ETL |
Extract Transform Load |
Извлечение Трансформация Загрузка |
|
IDE |
Integrated Development Environment |
Интегрированная среда разработки |
|
MPL |
Message Passing Layer |
Слой передачи сообщений |
|
OS |
Operating System |
Операционная система |
|
PDE |
Parallel Database Extensions |
Расширение для баз параллельной обработки данных |
|
PE |
Parsing Engine |
Парсинговый движок |
|
PI |
Primary Index |
Первичный индекс |
|
SQL |
Structured Query Language |
Структурированный язык запросов |
Таблица 2 Расшифровка русскоязычных сокращений
Сокращение |
Расшифровка |
|
БД |
База Данных |
|
РСУБД |
Реляционная система управления базами данных |
|
СУБД |
Система управления базами данных |
|
ХД |
Хранилище Данных |
Введение
Актуальность. В настоящее время трудно найти фирму, которая не использовала бы базы данных в той или иной форме - учет сотрудников, клиентов, продаж. Крупным же компаниям, у которых имеется несколько различных систем для учета данных, чтобы связать и проанализировать информацию, имеющуюся в различных источниках, требуется для начала построить хранилище данных (ХД) - ориентированную на нужды компании систему, позволяющую снять нагрузку, связанную с аналитикой, с систем-источников, обогатить, сопоставить данные между собой и подготовить исходные данные для отчетов и Business Intelligence надстроек.
В ХД попадают данные из самых разных источников - всё, что может представлять интерес при анализе может быть загружено в систему [1][2]. Основной объем занимают данные из оперативных БД, но могут быть задействованы различные структурированные и неструктурированные файлы, источники из сети Интернет.
С развитием хранилища - добавлением новых источников, увеличением объема данных, разработкой новых витрин и ETL-процессов, их загружающих, всё критичнее становятся вопросы оптимизации хранения информации, а также непосредственно процессов загрузки. Проблема состоит в том, что при разработке ETL-процесса весьма ограничено время на то, чтобы полностью оценить полученный SQL-скрипт и посмотреть, можно ли в нем что-то улучшить, чтобы промежуточные данные занимали меньше места, а сама работа потребляла как можно меньше ресурсов CPU и временной памяти.
Практически все современные РСУБД имеют встроенные оптимизаторы, но они работают лишь в пределах одного запроса - выбирают наилучший метод доступа к данным, алгоритмы и порядок соединения таблиц.
Оптимизация ETL-процесса же сводится к тому, чтобы оценить и картину в общем - проверить, как сочетается между собой хранение промежуточных данных, нет ли каких-то лишних действий, как, например, случайно оставленный в ранних версиях скрипта join на таблицу, которая по факту не используется, или же протягивание не нужных далее столбцов, повторение кода внутри скрипта.
Часть работ по определению мест в ETL-процессе, которые можно было бы улучшить, поддается автоматизации, но пока нет открытых программных средств, позволяющих обнаружить и скорректировать проблемы SQL-скриптов.
Наличие инструмента для предобработки скриптов позволило бы сократить трудозатраты на подготовку к разработке и повысить уровень качества ETL-процессов.
Цели и задачи
Цель данной дипломной работы - реализовать программное средство, позволяющее оптимизировать SQL-скрипты, написанные на диалекте языка, разработанного для СУБД Teradata.
Задачи:
1. Изучить архитектурные особенности СУБД Teradata
2. Изучить особенности диалекта SQL, используемого в данной СУБД
3. Исследовать аналоги, выявить их достоинства и недостатки
4. Выявить требования к программе
5. Разработать архитектуру приложения
6. Разработать пользовательский интерфейс
7. Реализовать парсинг SQL-скриптов
8. Реализовать оптимизацию скриптов
9. Реализовать возможность работы программы в различных режимах - без подключения к базе и без других исходных данных, с исходными данными, полученными от пользователя, с исходными данными, полученными через соединение с базой данных.
10. Разработать техническую документацию
Подробнее про пункт 9 списка задач.
Есть риск, что при использовании программы не у всех пользователей будет возможность настроить подключение к базе, поэтому надо учесть такую возможность и предоставить 3 различных режима работы:
- Нет возможности подключиться из программы к базе, и у пользователя нет доступа к базе в данный момент - простой синтаксический разбор и оптимизация
- Нет возможности подключиться из программы к базе, но сам пользователь может предоставить требующуюся информацию (разметка таблиц, распределение данных в системе)
- Есть возможность подключиться к базе, в этом случае программа будет забирать все необходимые ей данные сама.
Работа состоит из трех основных частей - обзора предметной области (глава 1), описания парсера (глава 2) и оптимизатора (глава 3), разработанных в рамках дипломного проекта. В первой части рассматриваются особенности архитектуры Teradata и факторы, влияющие на потребление таких ресурсов системы, как CPU и дисковое пространство. Во второй приводится описание архитектуры приложения, и основных классов, связанных с парсером. В третьей части более детально описывается оптимизация скриптов, которая проводится разработанным программным средством. Приложения включают в себя примеры SQL-кода, который можно оптимизировать по критериям, рассмотренным в главе 3 (приложение), и техническую документацию к проекту, а именно: техническое задание, руководство оператора, программа и методика испытаний, текст программы.
1. Анализ предметной области
1.1 Архитектура Teradata
Teradata Database - система массовой параллельной обработки данных. Поставляется она в виде комплекса оборудования и программного обеспечения. Также компания выделяет несколько специалистов, которые занимаются настройкой, техническим обслуживанием системы, мониторингом производительности, и могут консультировать разработчиков в случае необходимости.
Хранилища данных на основе СУБД Teradata имеют архитектуру «shared-nothing» - архитектуру распределенной обработки данных, где каждый узел независим, имеет свое дисковое пространство и вычислительную мощность [3]. Такая архитектура хорошо подходит для работы с большим объемом данных, так как легко масштабируется - добавление нового узла в систему требует минимум усилий.
Рис. 1 Верхнеуровневая архитектура СУБД Teradata
На рис. 1 показано устройство верхнеуровневой архитектуры Teradata. Рассмотрим компоненты, изображенные на нём, подробнее. Основные элементы архитектуры Teradata - Nodes (узлы) и BYNET-соединение между ними. Каждый из узлов представляет из себя отдельную машину с установленной OS и Parallel Database Extensions software.
Рассмотрим архитектуру детальнее, как показано на рис. 2.
Рис. 2 Архитектура СУБД Teradata
Основные компоненты архитектуры Teradata:
- PE - Parsing Engine
- MPL - Message Passing Layer
- AMP - Access Module Processor
- Vdisk - Virtual disk
Подробнее о каждой из них.
Parsing Engine отвечает за получение информации от пользователя (данные, SQL-скрипты), разбор и передачу инструкций на AMP'ы.
Работа PE состоит из четырёх основных частей:
? Session Control - контроль сессии - проверяет настройки пользовательской сессии, проводит авторизацию пользователя.
? Parser - парсер - интерпретирует SQL-запрос, анализирует его на наличие синтаксических ошибок, проверяет по системной информации наличие всех необходимых объектов и полей в таблицах/представлениях
? Optimizer - оптимизатор - оценивает все возможные варианты и разрабатывает наименее затратный по времени план доступа к данным. После этого план запроса переводится в исполняемые шаги и передается диспетчеру.
? Dispatcher - диспетчер - контролирует последовательность исполнения команд, передает команды на слой передачи данных. Он включает в себя блоки контроля выполнения задач и контроля возвращаемого результата. Первый получает команды от оптимизатора, транслирует команды на соответствующие AMP'ы, получает отчеты о статусе выполнения команд, и передает данные блоку контроля результата после того, как все задействованные AMP'ы завершили работу. Также перед передачей очередной команды он проверяет, что предыдущий шаг был завершен на всех узлах. Второй блок возвращает результат пользователю.
Message Passing Layer - комбинация BYNET-соединения и PDE ПО. Передает команды или данные от PE на AMP'ы и возвращает результаты или статусы выполнения команд с AMP'ов на PE.
Access Module Processor - основной рабочий элемент системы. Он является ответственным за связанный c ним блок данных и выполняет всю работу, нужную, чтобы получить результат - сортировка, агрегирование, форматирование, конвертация, объединение данных из разных таблиц.
Virtual disk - дисковое пространство, выделенное для AMP'а, все данные таблиц, определенные на связанный AMP, хранятся в этой области.
1.2 Встроенный оптимизатор в Teradata
СУБД Teradata имеет встроенный оптимизатор, который отвечает за выбор: интерфейс teradata парсер
? Способа доступа к данным - будет ли обращение к одному, нескольким или всем AMP'ам
? Способа соединения таблиц - подходящий алгоритм соединения - product join (он же nested loops join), hash join или merge join
? Места соединения - следует ли перераспределять данные между AMP'ами либо дублировать меньшую таблицу на все AMP'ы
? Порядка соединения таблиц - выбирается порядок, при котором будет меньше всего перераспределений данных и, соответственно, наименьшее время работы.
Оптимизатор выбирает самый быстрый метод доступа, сравнивая стоимость различных планов выполнения SQL-запроса, и, на основании доступной ему статистики, выбирает лучший.
Для составления лучшего плана ему необходима информация:
? Информация об окружении
_ Количество узлов (nodes)
_ Количество AMP'ов
_ Количество и тип CPU
_ Информация о дисковом массиве
_ Информация о внутреннем соединении (BYNET)
_ Количество доступной памяти
? Демография данных
_ Количество строк в таблице
_ Размер строки
_ Колоночная демография (диапазон значений, количество строк на значение, количество NULL'ов для колонки)
_ Демография индексов
Кроме этого, оптимизатор использует статистику по таблицам. Статистика представляет собой информацию о хранимых данных: сколько уникальных значений приходится на поля или комбинации полей, как значения распределяются по интервалам. В сущности, статистика представляет собой гистограмму: есть несколько интервалов (в Teradata 14 их 250 по умолчанию, можно увеличить до 500) [4], для каждого из которых считается, сколько в нём значений.
Если статистика собрана, то оптимизатор использует ее, иначе - для индексированных колонок используются случайные выборки данных с AMP'ов, для неиндексированных - эвристика (формулы).
Однако, пользоваться статистикой тоже надо осторожно. Следует поддерживать ее в актуальном состоянии, так как если статистика устаревает, а данные в таблице являются достаточно изменчивыми, то будет сформирован некорректный план выполнения, что может привести к тому, что один запрос занимает практически все ресурсы системы, блокируя работу остальных пользователей. При глобальных изменениях рекомендуется пересобирать статистику [4].
1.3 Оптимизация запросов в Teradata
Встроенный оптимизатор запросов в Teradata может значительно ускорить запрос по сравнению тем, как если бы команды выполнялись ровно так, как подает пользователь, однако, он не может влиять на работу скриптов в целом.
При написании запроса следует учитывать такие факторы как первичные и вторичные индексы исходных таблиц, первичный индекс итоговой таблицы (если происходит вставка данных), распределение данных по AMP'ам, размер таблиц и условия соединения между ними.
Primary Index (PI) - первичный индекс - механизм доступа к данным в СУБД Teradata, его состав определяет, значения каких полей таблицы будут использованы при распределении данных по AMP'ам либо доступа к ним.
Первичный индекс часто путают с первичным ключом, рассмотрим эти понятия и их основные свойства.
Первичный ключ - логическая концепция. Он однозначно определяет строку в таблице [5]. Каждая таблица может иметь только один первичный ключ, нет ограничения на количество колонок, которое в него входят. Teradata не требуется, чтобы он был указан. В значениях первичного ключа не может встречаться NULL.
Первичный индекс - механизм доступа к данным. Каждая таблица может иметь не более одного первичного индекса (может быть определен NO PRIMARY INDEX - индекса нет). Индексы имеют ограничение на количество используемых колонок - 64. Индекс может быть как уникальным, так и неуникальным, может содержать в том числе NULL-значения.
По значению PI на этапе обработки запроса диспетчером Parsing Engine определяется, на каком AMP лежат нужные данные, либо куда надо положить новые. Делается с помощью собственного алгоритма хэширования, разработанного компанией Teradata, работающего на разных типах данных - числа, строки, даты...
AMP определяется следующим образом [6]:
- вычисляем 32-битный хэш от значений PI данной строки (в PI может быть как одна колонка, так и несколько)
- согласно значениям первых 16 или 20 бит (в зависимости от того, сколько бит определяют ячейку) получаем ячейку в хэш-карте (bucket)
- получаем из найденной ячейки значение, соответствующее номеру AMP'а.
На схеме (рис. 3) это выглядит так:
Рис. 3 Определение AMP по значению PI
Пример хэш-карты для данного случая показан в таблице 3:
Таблица 3 Пример hash-карты для определения AMP
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
A |
B |
C |
D |
E |
F |
||
CE86 |
16 |
3 |
8 |
15 |
9 |
7 |
14 |
15 |
2 |
8 |
12 |
1 |
4 |
6 |
11 |
14 |
|
CE87 |
9 |
16 |
6 |
4 |
3 |
2 |
8 |
13 |
9 |
5 |
10 |
7 |
4 |
13 |
5 |
1 |
|
CE88 |
12 |
6 |
10 |
11 |
2 |
10 |
15 |
3 |
14 |
1 |
11 |
7 |
16 |
5 |
13 |
7 |
Хэш-карты формируются стандартными алгоритмами Teradata. Для системы с 16 AMP будет одна, для систем с 64 - другая. Для второй же системы с 64 AMP будет точно такая же, если ячейка карты определяется таким же количеством бит.
Оставшиеся биты в хэше строки будут использованы для нахождения ее на AMP'е.
Такое устройство хэш-карты удобно тем, что при добавлении новых узлов в систему не будет требоваться изменение алгоритмов хэширования, просто будет скорректирована карта и перераспределены затронутые данные.
При выборе первичного индекса надо учитывать 2 основных момента: насколько равномерно с помощью него будут разложены данные по AMP'ам и будут ли использоваться все его поля для получения информации.
С распределением данных по AMP'ам тесно связано понятие «перекошенности» таблиц - skew. Эта величина представляет собой процентный показатель и вычисляется по формуле 1, где AverageAMPSize и MaximumAMPSize - соответственно, средний и максимальный объемы данных, распределенных на AMP:
Формула 1 Вычисление процентного показателя skew таблицы
,
В СУБД Teradata каждый AMP содержит часть данных каждой таблицы (при количестве записей в таблице большем, чем количество AMP'ов).
В идеальном случае, когда PI подобран правильно, распределение строк по узлам системы будет равномерным. В такой ситуации при полном просмотре таблицы каждый узел будет выполнять одинаковую часть работы. Степень уникальности первичного индекса тут играет важную роль.
Если первичный индекс не уникален и на какие-то значения приходится во много раз большее количество строк (рис. 4), чем на другие, на соответствующих им AMP'ам закончится место для хранения информации.
Рис. 4 Неравномерное распределение данных
В данной ситуации AMP 1 заполнен полностью, в то время как остальные - примерно на 30%. Несмотря на то, что свободное место в системе, фактически, еще имеется, из-за его отсутствия на одном AMP при попытке добавить новые данные будет выведена ошибка о недостатке дискового пространства.
Второй момент, который надо учитывать при выборе индекса - доступ к данным. Не всегда уникальное по значению поле будет лучшим кандидатом для PI.
Parsing Engine во время составления команд определяет самый лучший метод доступа к данным - задействовать один AMP, несколько или все.
Если в запросе имеется условие равенства на поля из PI (причем задействованы должны быть именно все поля), то будет выбран самый удобный метод - по индексу, если же такого условия нет или задействованы не все поля, будет необходим полный просмотр таблицы.
Предположим, что у нас есть таблица “Заказы клиента”. Помимо прочих полей в нем есть ID заказа и ID клиента. ID заказа - PK, значения уникальны. ID клиента может повторяться, но тоже достаточно уникально. Мы знаем, что заказы нам интересны только в разрезе клиента - самый частый случай использования таблицы будет с фильтром “WHERE CUSTOMER_ID = ... ”. В таком случае будет удобнее создать таблицу с первичным индексом “CUSTOMER_ID”.
В зависимости от выбора индексов будут меняться и алгоритмы физического соединения таблиц. СУБД Teradata использует 3 основных варианта соединений - product join, hash join и merge join.
Product join (другое название - nested loops join) - алгоритм соединения вложенными циклами. Самый простой, хорошо работает, если обе таблицы маленькие. Он выбирается оптимизатором, когда в условии соединения не используется PI, либо используется, но с условием неравенства.
Hash join - алгоритм соединения таблиц, который строит по меньшей таблице хэш-карту, проходит по большей и по карте выбирает соответствующие строки. Удобен при соединении небольших таблиц на большие.
Merge join - алгоритм соединения, сначала сортирующий данные в таблицах (используется сортировка слиянием), после этого проходится по отсортированным данным и соединяет по совпадающим значениям. Является наименее ресурсоемким алгоритмом соединения, особенно при работе с большими таблицами.
Для объединения таблиц может потребоваться переложить данные одной из них по другому условию. Встроенный оптимизатор при выборе географии соединения оценивает размеры таблиц и может выбрать один из следующих вариантов:
- Не перекладывать данные - наилучший вариант, в таком случае PI у соединяемых таблиц совпадает, и можно сразу приступать к соединению, т.к. нужные уже лежат на нужных AMP'ах.
- Перераспределить данные - такой вариант будет использован, если обе таблицы большие, а PI у них разный. Данные из меньшей таблицы будут скопированы и переложены под другой индекс, а после того, как отработает соединение, вычищены из временного пространства.
- Дублировать данные - этот вариант будет использован, если одна из таблиц существенно меньше, чем другая, причем в соединении не используется PI большей. Такой способ обычно применяется для обогащения информации большей таблицы данными из сравнительно маленьких справочных витрин.
1.4 Обзор аналогов
Создание оптимизирующего препроцессора ограничивается несколькими условиями:
1. Частично написание и тестирование программы проводится на рабочих местах, где средства разработки весьма ограничены - из-за проблем безопасности установка стороннего программного обеспечения запрещена, можно использовать лишь уже имеющуюся IDE - Visual Studio, и, соответственно, языки программирования, связанные с .NET платформой.
2. Разрабатываемый продукт должен учитывать особенности диалекта SQL СУБД Teradata.
Рассмотрим доступные аналоги с учетом обозначенных выше ограничений.
Оптимизирующая обработка предполагает наличие двух основных блоков в программе - парсер (разбор кода) и оптимизатор (корректировка).
Рассмотрим подходящие продукты, которые можно было бы использовать при написании препроцессора для SQL-скриптов.
Парсер.
Из доступных аналогов можно выделить следующие продукты и библиотеки:
- SQL Parser от Sergey Gorbenko [7]
- Irony parser [8]
- General SQL Parser [9]
- JSQLParser [10]
Рассмотрим каждый из них отдельно.
SQL Parser от Sergey Gorbenko
Самый простой из представленных парсеров. Представляет собой .NET-приложение, позволяющее модифицировать select-запросы, добавляя новые условия в where и order by части запроса.
Irony parser
Парсер, который на основе дополнительного файла, описывающего грамматику языка, разбирает код на древовидную структуру. В текущей конфигурации не умеет работать с более чем одним sql-выражением при анализе.
General SQL Parser
Самый сложный из представленных аналогов, коммерческое решение, позволяющее парсить скрипты, проводить рефакторинг кода - изменять названия колонок, засекать sql-инъекции, и много других функций. Представляет собой несколько библиотек, доступен для java, .NET и несколько других версий. Специфическая обработка для Teradata-скриптов имеется в версиях, написанных на java и ANSI-C.
JSQLParser
Данный парсер представляет SQL-запросы в виде java-классов. Поддерживает обработку скриптов, но не может работать с особенностями синтаксиса диалекта SQL в Teradata.
Таблица 4 Сравнительный анализ аналогов
Критерий |
Sergey Gorbenko SQL Parser |
Irony Parser |
General SQL Parser |
JSQLParser |
|
Может обрабатывать SQL-скрипты, а не один запрос |
- |
- |
+ |
+ |
|
Учитывает особенности синтаксиса SQL для Teradata |
- |
- |
+ |
- |
|
Свободная лицензия |
+ |
+ |
- |
+ |
|
.NET-платформа |
+ |
+ |
+ |
- |
Итак, из доступных парсеров возможностью обработки SQL-скриптов обладают только General SQL Parser и JSQLParser. Irony Parser оказался неспособным обрабатывать некоторые конструкции языка - условие сравнения выражений “between” и псевдонимы колонок и таблиц. SQL Parser от Sergey Gorbenko является очень узконаправленным, поэтому использование его не представляется возможным.
Из всех представленных парсеров особенности диалекта SQL, используемого в Teradata учитывает только General SQL Parser. Однако, этот продукт не распространяется свободно, приобрести лицензионную версию можно за 899$, отдельно для Teradata (но java-версию) - за 299$ [10].
Доступных решений, позволяющих оптимизировать SQL-скрипты, найти не удалось.
2. Парсер
В приложении можно выделить 2 основных функциональных блока: парсер и оптимизатор. Данная глава посвящена первому из них.
Задача парсера - разобрать исходный SQL-скрипт и уложить каждый его шаг в объект специально разработанного класса, список таких объектов будет представлять собой направленный граф.
Разрабатываемая программа рассчитана на то, чтобы оптимизировать скрипты, на основе которых далее будут строиться ETL-процессы, это ограничивает утверждения, которые надо разбирать до «create table as select». Остальные операции, присутствующие в скрипте, как правило, носят разовый характер и не должны учитываться.
Ранее, в разделе «Цели и задачи», говорилось о том, что программа должна работать в трех режимах.
Первый - самый простой, анализ скрипта только лишь на основе имеющегося кода. Используется в случае, если из программы нельзя подключиться к базе, а у пользователя нет возможности предоставить нужные исходные данные - DDL исходных таблиц, размер и степень неравномерности распределения промежуточных.
При данном режиме накладываются ограничения и на разбор SQL-скриптов. Так, при выборе из исходных таблиц (не промежуточных) не допускается конструкция «select *».
Второй - анализ скрипта на основе имеющегося кода и дополнительных исходных данных. Если у программы нет возможности подключиться к базе самостоятельно, для парсера дополнительным преимуществом будет «знание» DDL объектов - определение исходных таблиц со списком их полей и индексами. При наличии такой информации можно разобрать конструкцию «select *».
Третий режим - программа может подсоединиться к базе сама. В данном случае пользователю не надо будет дополнительно собирать данные, что является куда более удобным решением.
В программе есть 3 основных типа объектов, представляющих собой таблицы - TableNode (таблица-узел), TableStep (таблица-шаг) и TableStepUnion (шаг-объединение).
Все объекты этих типов хранятся в списке, по которому можно построить направленный граф (пример на рис. 5).
Рис. 5 Способ представления SQL-скрипта в виде графа
Класс TableStep наследуется от класса TableNode и расширяет его. Класс TableStepUnion также наследует класс TableNode, но в качестве исходных таблиц он использует только TableStep-объекты.
Рассмотрим атрибуты, необходимые в объектах этих классов для полного разбора и дальнейшей оптимизации.
TableNode - класс, представляющий таблицу без учета того, из каких источников и как она собирается.
- Название схемы БД, в которой лежит таблица
- Имя таблицы
- Список колонок
- PI
Все атрибуты представляют собой строки (имя схемы или таблицы) или списки строк (список колонок, PI)
Класс TableStep наследует атрибуты класса TableNode и добавляет новые, чтобы сохранить то, как именно создается данная таблица.
До описания атрибутов объектов этого класса рассмотрим структуру SQL-запросов на выборку данных в СУБД Teradata.
- SELECT - список выражений
- FROM - список исходных таблиц и условий их соединения
- WHERE - дополнительные условия на отдельные строки итогового результата
- GROUP BY - список полей/выражений, по которым надо группировать
- HAVING - условия на группы
- ORDER BY - упорядочивание вывода строк (только в простом select)
- QUALIFY - условия на аналитические функции
QUALIFY - особенность диалекта SQL, используемого в Teradata. Выражения, использующиеся в этой части - аналог части having, но для аналитических функций. Такой вид функций позволяет работать с группой данных, при этом не прибегая к группировке, что в некоторых ситуациях значительно упрощает скрипты, уменьшая количество необходимых для достижения результата шагов. Конструкция qualify же позволяет отфильтровать неподходящие записи при том же чтении из таблицы, тогда как в oracle, например, для этого понадобится дополнительная выборка с условием where на результат аналитической функции.
Еще одна особенность диалекта SQL Teradata - возможность использования в GROUP BY номеров колонок из части SELECT. Это позволяет сократить длину запроса, но иногда делает его менее удобным для чтения, особенно если SELECT-список достаточно длинный. Так вместо GROUP BY FIELD_1, FIELD_2, FIELD_3 можно написать GROUP BY 1,2,3. Цифры, названия колонок и выражения можно использовать в произвольном сочетании.
Из представленной выше структуры в исходных SQL-скриптах могут встречаться все конструкции, кроме ORDER BY. Из остальных обязательно должны присутствовать только SELECT и FROM, а HAVING может быть только при наличии GROUP BY.
Класс TableStepUnion наследует класс TableNode. Кроме атрибутов основного класса он содержит только список TableStep-шагов (приведенных к типу TableNode) и то, какой именно операцией эти исходные таблицы объединяются: union (all) - объединение (all - не убирая дубли из финального результата), except (all) - исключение результата второй выборки из первой, intersect (all) - данные, совпадающие в витринах, minus аналог Teradata для except. На практике очень часто intersect, except и minus реализовываются с помощью inner или left объединений, тогда основным случаем использования типа таблиц TableStepUnion становится именно объединение данных.
Считается, что скрипт, который подается на оптимизацию, синтаксически корректен. Парсер не должен заниматься проверкой синтаксиса, однако, если что-то не получится разобрать, он выдаст ошибку с указанием примерного места и части запроса/скрипта, в которой произошла ошибка.
В таблице 5 приведено описание основных классов, используемых для реализации хранения в программе SQL-кода (часть TableStep), и их назначения:
Таблица 5 Краткое описание основных классов в TableStep
Класс |
Краткое описание |
|
TableSelect |
«Обертка» для класса TableNode, к таблице приписывается псевдоним. |
|
TableColumn |
Название колонки и таблица (TableSelect), из которой она выбирается |
|
Expression |
Выражение - текст и список колонок, которые в этом выражении используются. В самом тексте колонки заменены на маркеры вида «[0]». |
|
SelectColumn |
Колонки в select-списке. Представляет собой выражение (Expression) и псевдоним (при наличии). |
|
Condition |
Условие. Объекты данного класса могут использоваться в QUALIFY, WHERE, HAVING и внутри FROM в соединениях таблиц |
|
JoinCondition |
Условие соединения таблиц. Тип Join'а и список условий (Condition) при их наличии. |
После выделения из шага скрипта select-части она представляется в следующем виде:
- select expression_list:
- expression alias
- …
- from join_list:
- left table join right table on condition_list
- left_expression condition right_expression
- where condition_list
- left_expression condition right_expression
- group by expression_list (ссылки на выражения из select)
- having condition_list
- qualify condition_list
Для разбора SQL-скриптов были разработаны 2 класса: SQLСleanUp и SQLParser. Первый принимает на вход текст скрипта целиком, очищает его от мусора (в т.ч. не интересующие нас для оптимизации шаги).
Такая структура близка к способу хранения информации о ETL-процессе в инструменте для их построения - SAS Data Integration Studio, поэтому данный парсер может быть в дальнейшем использован и для других проектов, в частности - автоматической сборки простейших ETL по SQL-коду.
3. Оптимизатор
Задача оптимизатора в рамках данной дипломной работы - исправлять части SQL-кода, которые могут приводить к дополнительным тратам памяти и ресурсов.
На появление подобных проблем могут влиять следующие ошибки, допущенные при написании кода:
· Наличие промежуточных шагов, которые не используются при создании финальных витрин
· Протянутые в промежуточные таблицы поля, не нужные для расчета финальных показателей
· Соединения на таблицы, поля из которых не используются в текущем шаге
· Выбор индекса, использование которого приводит к значительному перекосу в хранении данных или к тратам дополнительных ресурсов CPU при перекладывании таблицы для ее соединения с другими объектами далее в скрипте
· Повторяющийся код
Рассмотрим каждую из этих ситуаций и методы, используемые в разработанном приложении для их решения.
1. Забытые промежуточные шаги
Многие задачи имеют более одного пути решения, так и при написании SQL-кода поля финальных витрин часто могут быть получены несколькими способами: рассчитаны в другом порядке, взяты из разных исходных витрин. Если какой-то из способов оказался недостаточно точным по мнению заказчика, или проигрывает другому по каким-то иным показателям, данные промежуточного шага, рассчитывающего первое значение, могут перестать использоваться. Если при этом сама таблица не была убрана из скрипта, она будет занимать дополнительное место.
В качестве примера можно привести простой отчет по балансам новых клиентов (Приложение).
Исходные данные:
- AGG_DB.CLIENTS - информация по клиентам
- DET_DB.CLIENT_ACCOUNT - связка клиент-счет
- DET_DB.ACCOUNT_BALANCE - баланс на счету (детальная информация)
- AGG_DB.CLIENT_BAL_MONTHLY - баланс на счетах клиента на конец месяца (агрегированная информация)
Первый шаг, выбирает новых клиентов (TEST_DB.CLIENTS_NEW), далее надо привязать к ним балансы. Если заказчик хочет получать такой отчет раз в месяц, на конец месяца, то можно выбрать данные как из детального слоя, так и из агрегированного. В ходе тестирования решили, что использовать витрины агрегированного слоя предпочтительнее - они занимают меньший объем данных, соединения будут легче, а шаг с выбором детальной информации не убрали.
Разработанное программное средство определяет такие случаи. После разбора скрипта те таблицы, которые нигде не используются, помечаются как финальные. Далее у пользователя запрашивается (рис. 6.), какие из таблиц являются итоговыми на самом деле.
Рис. 6 Запрос на выделение финальных витрин
Если найденная таблица не является целевой и пользователь снимет отметку об этом, она будет убрана из скрипта.
2. Лишние поля в промежуточных таблицах
Иногда в процессе тестирования скрипта требуется дотянуть в промежуточные таблицы поля-источники для расчета показателей (если сами эти поля не должны отображаться в итоге) или прочую дополнительную информацию для тестирования, чтобы понять, корректным или нет получился результат. Потом эти поля могут быть оставлены и, соответственно, занимать место, особенно длинные строки.
Пример - слегка модифицированная версия отчета о балансе новых клиентов на конец месяца (Приложение).
Исходные данные:
- AGG_DB.CLIENTS - информация по клиентам
- AGG_DB.CLIENT_BAL_MONTHLY - баланс на счетах клиента на конец месяца (агрегированная информация)
В первом шаге также выбирается информация по новым клиентам, во втором - подтягивается баланс. Допустим, в какой-то момент считалось, что в отчете должно быть поле ИНН - IND_TAX_PAYER_NUM, потом же от этой идеи отказались, из финальной таблицы поле убрали, а из промежуточной - нет.
Оптимизатор, написанный в рамках этой работы, определяет список полей, которые используются во всех зависимых от таблицы витринах (если это промежуточный шаг), и убирает лишние.
3. Соединение на неиспользуемые таблицы
Если шаг прототипа содержит большое количество соединений для обогащения данных (в таком случае к основной таблице с помощью left join присоединяется другая), при тестировании, аналогично ситуации с полями, могут быть оставлены таблицы, данные из которых фактически не нужны.
Другой вид соединения - inner join - может в таком случае играть роль фильтра - отсекать записи одной из таблиц, которым нет соответствия в другой, а left join, при котором данные присоединяемой таблицы не используются ни в select, ни в последующих соединениях, ни в фильтрации, на самом деле оказывается просто лишней тратой ресурсов. Такой join может привести к слабо контролируемому дублированию данных, источник которого сложно найти, что будет влиять на результаты и сроки тестирования.
В качестве примера возьмем выборку по клиентам (Приложение).
Исходные данные:
- AGG_DB.CLIENTS - информация по клиентам
- AGG_DB.CLIENTS_DETAILED - дополнительная информация по клиентам, доступ к которой производится нечасто, из-за чего ее вынесли отдельно.
Допустим, сначала планировалось, что отчет должен содержать в себе также информацию, необходимую для расчета кредитных рисков - количество членов семьи в общем, а также детей. Позже решили вынести такую информацию в отдельный отчет, а в текущей версии скрипта соединение не убрали.
Разработанный оптимизатор определяет тип соединения, используется ли присоединяемая таблица в текущем шаге, и, если она не используется при соединении типа left join, убирает ее.
4. Неоптимальный выбор индекса
Выбор первичного индекса - один из ключевых моментов при разработке sql-скриптов для Teradata. В зависимости от него будет определено, какой метод соединения будет выбран, и где будут соединяться таблицы - будет ли дополнительное перераспределение или дублирование данных по всем узлам.
При выборе индекса для промежуточного шага надо учесть следующие факторы:
· По каким полям будет в дальнейшем происходить обращение к таблице
· По каким полям происходит соединение внутри шага
· Какие поля используются при группировке
· Какие поля используются в части partition by конструкции qualify (если эта часть есть, то она будет выполняться последней, и эти поля будут наиболее вероятными кандидатами для PI, особенно если таблица в дальнейшем используется по одному из них).
Для работы оптимизатора желательно иметь подключение к базе, тогда можно будет с большей точностью определить, насколько хорошо выбраны индексы для таблиц.
Если нет подключения к базе, можно приблизительно описать, какого примерно размера будут исходные таблицы. Это повлияет на выбор PI.
Так, оптимизатору подается файл с описанием шаблонов названий исходных таблиц и того, сколько они могут занимать места.
Во многих хранилищах данных производится унификация названий витрин - ручные справочники, детальная информация, агрегированная информация, факты, измерения. Таблицы справочников, которые заполняются вручную, должны быть совсем небольшими, агрегированные витрины - побольше, измерения и факты, как правило, самые большие.
Можно выделить несколько шаблонов названий и оценить по шкале (для простоты - от 1 до 3), каким будет размер этих таблиц относительно друг друга.
На работу оптимизатора также будет влиять, в каком из трех режимов работает программа. Рассмотрим, как именно.
Три режима работы (применительно к оптимизации)
· Первый - самый простой, просто анализ синтаксиса запросов.
В этом режиме работы программе можно указать только приблизительные размеры таблиц относительно друг друга. Эта настройка разовая - не требуется при каждом запуске вводить нужные значения.
· Второй - плюс чтение данных из файла
В режиме работы с чтением данных из файла появляется большая свобода действий как для парсера, так и для оптимизатора.
Файлы, которые потребуются для работы программы в данном режиме:
- Поля, имеющиеся в исходных таблицах
Этот файл представляет собой список полей в каждой из исходных таблиц. Его наличие позволяет разобрать конструкции вида «select * from» и оптимизировать их, потому как часто не все поля, которые выбираются таким образом, особенно на начальных этапах, используются далее в скрипте.
Данные по полям таблиц и представлений содержатся в системной информации Teradata - представлении DBC.ColumnsV.
- Индексы таблиц, их размеры и skew
В данном файле должна содержаться информация по каждой таблице (и исходной, и создаваемой скриптом) - ее название, индекс, размер в Мб и фактор перекоса.
Вся эта информация также содержится в системных таблицах Teradata. Индексы - DBC.Indices, размеры таблиц - DBC.TableSize. Показатель skew вычисляется на основе размеров таблиц.
· Третий - с подсоединением к базе
Данный режим позволяет учесть наибольшее количество факторов по сравнению с другими, плюс он не требует от пользователя ввода информации по индексам, размерам и полям - всё это он может программа получает сама. В дополнение к разбору этой информации появляется возможность проверять кандидатов на PI с помощью функций hashamp(hashbucket(hashrow())) и group by - смотреть, на какой из AMP'ов будут попадать данные при выборе PI, и как строки будут распределены между AMP'ами.
Подходящий для таблицы PI определяется по взвешенной сумме факторов. Поля анализируются по двум критериям: входные данные для витрины, какие поля используются в самом шаге, и витрина как исходная для следующих шагов - как будет использоваться в дальнейшем.
Анализ самого шага, для которого ищем индекс. Надо проверить, какие поля из центральной таблицы (обычно - первая таблица в части from) используются в соединении. Хорошие кандидаты для PI - условия равенства на поле (поля), присоединяемые по таким условиям таблицы должны быть либо больше, либо примерно того же размера, что и основная таблица. Если присоединяемая таблица маленького размера и есть возможность установить этот факт по каким-либо из исходных файлов или с помощью запроса напрямую к базе, полю отдается меньший приоритет.
Чем больше идет соединений на поле в текущем шаге, тем большим будет его вес. Также надо учитывать, откуда берется поле-кандидат на индекс. Если оно является PI в исходной витрине, то к весу поля добавляем дополнительные баллы.
Анализ зависимых от текущего шагов.
Здесь уже анализируется, как таблица будет использована дальше, чтобы по возможности избежать перераспределения данных. Если доступ к данным идет по тому же полю, что было выбрано в первой части, это хороший кандидат для PI. Если в первой части было выбрано одно поле, а доступ идёт по другому, при этом обращаемся мы к таблице всего один раз, без разницы, какой из вариантов выбрать - всё равно оптимизатор будет перекладывать данные. Если же к таблице идет более одного обращения по другому полю, лучше поменять исходный индекс, чтобы минимизировать количество перекладываний данных.
Если оптимизатор находит несколько кандидатов для PI, сообщение об этом выводится в комментарии к шагу.
Пример (Приложение).
Скрипт содержит формирование двух таблиц: счета клиентов (TEST_DB.CLIENT_ACC) и балансы на счете (TEST_DB.CLIENT_BAL). Первая таблица формируется путем соединения двух исходных таблиц по полю CLIENT_ID. Во второй же используется соединение по ACCOUNT_ID. PI первой таблицы в данном примере - (ACCOUNT_ID, CLIENT_ID). Такой индекс может давать более равномерное распределение данных по AMP - у каких-то клиентов счетов может быть много, если они попадут на один AMP, может быть перекос в хранении. Однако, такой PI в данном случае - не лучший выбор, потому что в первой таблице используется соединение по CLIENT_ID, и если таблицы большие, то они обе будут переложены под этот PI, соответственно, первый шаг будет удобно сразу оставить с таким же индексом, перекос в данном случае не должен быть сильно выражен. Также первой таблице можно поставить индекс ACCOUNT_ID, т.к. далее она будет использоваться по этому полю.
5. Повторяющийся код
Данный случай является не очень частым, но иногда бывает, что соединения одних и тех же таблиц в скрипте выполняются несколько раз.
Рассмотрим пример (Приложение - Исходный код).
Данный SQL-скрипт формирует отчет по типам кредитных договоров. Рассматриваются активные договоры - есть задолженность в текущем месяце, закрытые - в прошлом месяце была задолженность, а в этом погасили, и реструктурированные - в прошлом месяце была задолженность, в этом погасили, плюс появилась запись со связкой нового договора (после реструктуризации) и старого (до нее). Для каждого типа договора выбирается информация из исходной витрины - (AGG_DB.AGREEMENT_BALANCE_MONTH), активные - те, у которых есть задолженность, закрытые и реструктурированные - нет, для определения, закрыли ли договор или нет, идет соединение на таблицу реструктуризаций (AGG_DB.AGREEMENTS_RESTRUCT). Если нашли в ней соответствие, то договор был реструктурирован, иначе - просто закрыт. Такие таблицы могут быть достаточно большими. Чтобы не читать информацию много раз, можно сначала выбрать информацию о балансах, после чего сделать одно соединение на таблицу реструктуризаций вместо двух, а условия на балансы и наличие совпадений в витринах вынести далее в скрипт. Пример того, как данный скрипт можно улучшить по количеству просмотров исходных таблиц и количеству соединений также можно посмотреть в приложении (Исправленный код).
Оптимизатор выделяет код, повторяющийся в как можно большем количестве шагов и выносит подобные соединения в отдельные таблицы, сохраняя различающиеся части.
Все приведенные выше примеры являются упрощением реальных случаев, возникавших при разработке ETL-процессов.
Заключение
Оптимизация потребления ресурсов (хранение данных, ресурсы CPU) - важная задача при реализации ETL-процессов. Чем больше ресурсов системы будет свободно, тем дольше она не будет требовать подключения нового оборудования.
В ходе данной работы был разработан препроцессор SQL-скриптов, позволяющий автоматически исправить типичные проблемы, возникающие при разработке ETL-процессов:
Были решены все поставленные задачи, а именно:
1. Изучены архитектурные особенности СУБД Teradata и особенности диалекта SQL, используемого в данной СУБД
2. Исследованы аналоги, выявлены их достоинства и недостатки
3. Выявлены требования к программному средству
4. В соответствии с требованиями спроектирован и реализован препроцессор SQL-скриптов
5. Разработана техническая документация.
Направления дальнейшей работы включают в себя выявление новых проблем, которые могут быть исправлены данным программным средством, реализация на основе модулей программы новых приложений. Так, например, парсер может быть использован для конвертации SQL-скриптов в формат, используемый SAS Data Integration Studio для хранения ETL-процессов.
Источники
1. William H. Inmon. Building the data warehouse. - 4th edition. - Wiley Publishing, Inc., 2005. - С. 546.
2. Lawrence Corr, Jim Stagnitto, Agile Data Warehouse Design: Collaborative Dimensional Modeling, from Whiteboard to Star Schema, - DecisionOne Press, 2011 - С. 330.
3. Lee, Sunguk, Shared-Nothing vs. Shared-Disk Cloud Database Architecture // International Journal of Energy, Information and Communications // 2011. - vol. 2 №4. - C. 211-215
4. Habrahabr [Электронный ресурс]: Статистика в СУБД Teradata. - Teradata Company Blog. - Электрон. дан. - URL: https://habrahabr.ru/company/teradata/blog/167801/ (дата обращения: 25.04.2016)
5. К. Дж. Дейт. Введение в системы баз данных / Introduction to Database Systems. - 8-е изд. - М.: Вильямс, 2006. - С. 1328.
6. Habrahabr [Электронный ресурс]: Teradata - СУБД, параллельная от рождения. - Teradata Company Blog. - Электрон. дан. - URL: https://habrahabr.ru/company/teradata/blog/160821/ (дата обращения: 20.04.2016)
7. CodeProject [Электронный ресурс]: SQL Parser. - Sergey Gorbenko. - URL: http://www.codeproject.com/Articles/32524/SQL-Parser (дата обращения: 22.04.2016)
8. CodePlex [Электронный ресурс]: Irony Parser. - URL: https://irony.codeplex.com/ (дата обращения: 22.04.2016)
9. General SQL Parser [Электронный ресурс]. - URL: http://www.sqlparser.com/sql-parser-dotnet.php (дата обращения: 22.04.2016)
10. SourseForge [Электронный ресурс]: JSQLParser. - URL: http://jsqlparser.sourceforge.net/ (дата обращения: 22.04.2016)
Приложение
Примеры SQL-скриптов
Забытые ненужные промежуточные шаги
CREATE TABLE TEST_DB.CLIENTS_NEW AS (
SELECT
CAST('2016-05-31' AS DATE) AS REPORT_DATE,
src.CLIENT_ID,
src.CLIENT_FULL_NM
FROM
AGG_DB.CLIENTS src
WHERE
src.FIRST_MONTH_FLG = 'Y'
AND CAST('2016-01-31' AS DATE) BETWEEN src.ACTIVE_FROM_DT
AND src.ACTIVE_TO_DT
) WITH DATA PRIMARY INDEX (CLIENT_ID);
CREATE TABLE TEST_DB.CLIENT_BAL_DETAIL AS (
SELECT
src.REPORT_DATE,
src.CLIENT_ID,
src.CLIENT_FULL_NM,
bal.BALANCE_RUR
FROM
TEST_DB.CLIENTS_NEW src
INNER JOIN DET_DB.CLIENT_ACCOUNT ca
ON
(
src.CLIENT_ID = ca.CLIENT_ID
AND src.REPORT_DATE BETWEEN ca.ACTIVE_FROM_DT
AND ca.ACTIVE_TO_DT
)
INNER JOIN DET_DB.ACCOUNT_BALANCE bal
ON
(
ca.ACCOUNT_ID = bal.ACCOUNT_ID
AND src.REPORT_DATE BETWEEN bal.ACTIVE_FROM_DT
AND bal.ACTIVE_TO_DT
)
WHERE
bal.BALANCE_RUR >= 100000
) WITH DATA PRIMARY INDEX (CLIENT_ID);
CREATE TABLE TEST_DB.CLIENT_BAL_AGG AS (
SELECT
src.REPORT_DATE,
src.CLIENT_ID,
src.CLIENT_FULL_NM,
bal.BALANCE_RUR
FROM
TEST_DB.CLIENTS_NEW src
INNER JOIN AGG_DB.CLIENT_BAL_MONTHLY bal
ON
(
src.CLIENT_ID = bal.CLIENT_ID
AND src.REPORT_DATE = bal.REPORT_DATE
)
WHERE
bal.BALANCE_RUR >= 100000
) WITH DATA PRIMARY INDEX (CLIENT_ID);
Лишние поля в промежуточных таблицах
CREATE TABLE TEST_DB.CLIENTS_NEW AS (
SELECT
CAST('2016-05-31' AS DATE) AS REPORT_DATE,
src.CLIENT_ID,
src.CLIENT_FULL_NM,
src.IND_TAX_PAYER_NM
FROM
AGG_DB.CLIENTS src
WHERE
src.FIRST_MONTH_FLG = 'Y'
AND CAST('2016-05-31' AS DATE) BETWEEN src.ACTIVE_FROM_DT
AND src.ACTIVE_TO_DT
) WITH DATA PRIMARY INDEX (CLIENT_ID);
CREATE TABLE TEST_DB.CLIENT_BAL_AGG AS (
SELECT
src.REPORT_DATE,
src.CLIENT_ID,
src.CLIENT_FULL_NM,
bal.BALANCE_RUR
FROM
TEST_DB.CLIENTS_NEW src
INNER JOIN AGG_DB.CLIENT_BAL_MONTHLY bal
ON
(
src.CLIENT_ID = bal.CLIENT_ID
AND src.REPORT_DATE = bal.REPORT_DATE
)
WHERE
bal.BALANCE_RUR >= 100000
) WITH DATA PRIMARY INDEX (CLIENT_ID);
Соединения на неиспользуемые таблицы
CREATE TABLE TEST_DB.CLIENTS_NEW AS (
SELECT
CAST('2016-05-31' AS DATE) AS REPORT_DATE,
src.CLIENT_ID,
src.CLIENT_FULL_NM
/*det.FAMILY_MEMBERS_CNT,
det.CHILDREN_CNT больше не надо*/
FROM
AGG_DB.CLIENTS src
LEFT JOIN AGG_DB.CLIENTS_DETAILED det
ON
(
src.CLIENT_ID = det.CLIENT_ID
AND CAST('2016-05-31' AS DATE) BETWEEN src.ACTIVE_FROM_DT
AND src.ACTIVE_TO_DT
)
WHERE
src.FIRST_MONTH_FLG = 'Y'
AND CAST('2016-05-31' AS DATE) BETWEEN src.ACTIVE_FROM_DT
AND src.ACTIVE_TO_DT
) WITH DATA PRIMARY INDEX (CLIENT_ID);
Неоптимальный выбор индекса
CREATE TABLE TEST_DB.CLIENT_ACC AS (
SELECT
CAST('2016-05-31' AS DATE) AS REPORT_DATE,
src.CLIENT_ID,
src.CLIENT_FULL_NM,
src.ACCOUNT_ID,
det.FAMILY_MEMBERS_CNT,
det.CHILDREN_CNT
FROM
AGG_DB.CLIENT_ACCOUNTS src
LEFT JOIN AGG_DB.CLIENTS_DETAILED det
ON
(
src.CLIENT_ID = det.CLIENT_ID
AND CAST('2016-05-31' AS DATE) BETWEEN det.ACTIVE_FROM_DT
AND det.ACTIVE_TO_DT
)
WHERE
AND CAST('2016-05-31' AS DATE) BETWEEN src.ACTIVE_FROM_DT
AND src.ACTIVE_TO_DT
) WITH DATA PRIMARY INDEX (CLIENT_ID, ACCOUNT_ID);
CREATE TABLE TEST_DB.CLIENT_BAL AS (
SELECT
src.REPORT_DATE,
src.CLIENT_ID,
src.CLIENT_FULL_NM,
src.FAMILY_MEMBERS_CNT,
src.CHILDREN_CNT,
Подобные документы
Разработка программного продукта - приложения, позволяющего заносить данные анкетирования в базу данных MS SQL. Описание логики работы приложения, особенности пользовательского интерфейса. Формы просмотра анкет, описание процедур и функций программы.
курсовая работа [1,2 M], добавлен 16.08.2012Учета жильцов студенческого общежития. Требования к программному средству. Спецификация качества программного обеспечения. Проектирование архитектуры приложения и структуры данных, пользовательского интерфейса. Спецификация классов и типы данных.
курсовая работа [664,4 K], добавлен 26.08.2012Требования к аппаратным и операционным ресурсам. Логическая и физическая организация. Состав основных классов проекта. Технико-экономическое обоснование разработки программного средства. Задержки при обработке данных. Разработка интерфейса приложения.
дипломная работа [4,4 M], добавлен 16.06.2017Ознакомление с программой проведения сборки компьютера из деталей, имеющихся в базе данных. Рассмотрение правил создания иерархии классов. Описание основных методов и пользовательского интерфейса. Изучение системных требований и текстов основных классов.
курсовая работа [710,2 K], добавлен 26.07.2014Характеристика основных потоков данных, существующих на предприятии. Способы и средства для разработки программного обеспечения. Проектирование пользовательского интерфейса. Разработка слоя взаимодействия с базой данных. Разработка слоя бизнес сервисов.
дипломная работа [750,8 K], добавлен 10.07.2017Имитационное моделирование деятельности "Центра обслуживания абонентов". Диаграммы потоков данных. Выявление вариантов использования. Моделирование видов деятельности и взаимодействий. Проектирование пользовательского интерфейса и архитектуры приложения.
дипломная работа [1,3 M], добавлен 24.10.2010Рассмотрение инфологической и даталогической модели базы данных кинотеатров города. Разработка базы данных в программе MS Access. Описание структуры приложения и интерфейса пользователя. Изучение SQL-запросов на вывод информации о кинотеатре и о фильме.
курсовая работа [1,1 M], добавлен 04.09.2014Характеристика объекта автоматизации. Создание многоуровневой архитектуры приложения, отладка метода безошибочной идентификации пользователей системы. Разработка нестандартного метода преобразования объектов базы данных в объекты классов приложения.
курсовая работа [395,4 K], добавлен 28.04.2015Специальное и общесистемное программное обеспечение. Разработка диаграммы прецедентов, классов, деятельности, состояний. Детальная реализация функциональных частей программного обеспечения. Разработка пользовательского интерфейса, методика испытаний.
курсовая работа [3,3 M], добавлен 26.01.2013Требования к подсистеме создания Scorm-пакетов. Построение диаграммы потоков данных. Проектирование программного средства. Выбор средств реализации подсистемы. Организация взаимодействия приложения с базой данных. Реализация пользовательского интерфейса.
курсовая работа [634,2 K], добавлен 16.08.2012