Постройте программу которая работает с базой данных в виде типизированного файла ваша субд
Создать программу, в которой создается база данных, содержащая Ф.И.О. сотрудника предприятия, номер трудовой книжки, дату поступления на работу, поощрения, наказания, дату увольнения, причину увольнения. В конце года подводится итог.
Создать программу, в которой создается база данных, содержащая Ф.И.О. сотрудника предприятия, номер трудовой книжки, дату поступления на работу, поощрения, наказания, дату увольнения, причину увольнения. В конце года подводится итог.
Программа должна предоставлять возможность просматривать, добавлять, удалять, копировать, хранить данные.
В программе использовать объекты и модули.
ВВЕДЕНИЕ
Для обработки базы данных будут использоваться списковые структуры.
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
В данной работе была создана элементарная база данных, для работы с которой используются следующие операции: создание списковой структуры, просмотр, удаление, сохранение в типизированном файле, восстановление из типизированного файла, поиск нужного элемента, создание отчета.
База данных[1]- это один или несколько файлов данных, предназначенных для хранения, изменения и обработки больших объемов взаимосвязанной информации.
Реляционной БД[2] называется такая БД, которая основана на реляционной модели данных.
Системы управления базами данных позволяют объединять большие объемы информации и обрабатывать их, сортировать, делать выборки по определённым критериям и т.п.
Простота использования СУБД позволяет создавать новые базы данных, не прибегая к программированию, а пользуясь только встроенными функциями.
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
СУБД обеспечивают правильность, полноту и непротиворечивость данных, а также удобный доступ к ним.
В данном проекте создается простейшая база данных, состоящая из одной таблицы. Классификация баз данных.
1. По типу хранимой информации БД делятся на
· Документальные. К ним относятся: библиографические, реферативные и полнотекстовые. В документальных БД единицей хранения является какой-либо документ (например, текст закона или статьи), и пользователю в ответ на его запрос выдается либо ссылка на документ, либо сам документ, в котором он может найти интересующую его информацию. В системах документального типа целью поиска может быть не только какая-то информация, хранящаяся в документах, но и сами документы. Так, возможны запросы типа «сколько документов было создано за определенный период времени» и т. п. Часто в критерий поиска в качестве признаков включаются «дата принятия документа», «кем принят» и другие «выходные данные» документов.
· Лексикографические. К ним относятся различные словари (классификаторы, многоязычные словари, словари основ слов и т. п.).
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
. По характеру организации хранения данных и обращения к ним различают:
· Общие (интегрированные, централизованные).
· Распределенные базы данных. Интегрированные и распределенные БД предполагают возможность одновременного обращения нескольких пользователей к одной и той же информации (многопользовательский, параллельный режим доступа). Это привносит специфические проблемы при их проектировании и в процессе эксплуатации БнД. Распределенные БД, кроме того, имеют характерные особенности, связанные с тем, что физически разные части БД могут быть расположены на разных ЭВМ, а логически, с точки зрения пользователя, они должны представлять собой единое целое.
. По характеру организации данных:
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
Этот классификационный признак относится к информации, представленной в символьном виде.
· Неструктурированные. К ним относятся базы данных, организованные в виду семантических сетей (Семантическая сеть -информационная модель предметной области, имеющая вид ориентированного графа, вершины которого соответствуют объектам предметной области, а дуги (рёбра) задают отношения между ними.).
· Структурированные. Структурированные БД требуют предварительного проектирования и описания структуры БД. Только после этого базы данных такого типа могут быть заполнены данными.
ГЛАВА 1 Типы данных, используемые в данной работе
база данные ввод отчет
1. Запись, содержащая Ф.И.О. сотрудника предприятия, номер трудовой книжки, дату поступления на работу, поощрения, наказания, дату и причину увольнения.
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
type //тип данных, сохраняемых в узле списка
fam,name,otch:string[20]; //фамилия, имя, отчество
nomer:integer; //номер трудовой книжки
data1,data2:string[20]; //дата поступления на работу, дата увольнения
poosh,nakaz,reason:string[40]; //поощрения, наказания, причина увольнения
. Описание типа указатель
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
. Тип узла двусвязного списка
x:zp; // поле, хранящее данные узла
next:puzel; // указатель на следующий узел списка
pred:puzel; // указатель на предыдущий узел списка
. Тип типизированного файла, в котором будут храниться данные списка
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
fzap1 = file of zp; Операции над списковой структурой, используемой в данной работе (описание процедур).
. Вставка узла в голову списка
procedure AddFirst(var f:puzel; a:puzel);
2. Вставка a в список после элемента, на который ссылается указатель old
AddAfter(var old:puzel; a:puzel);
3. Создание нового узла с нуля
procedure BuildSpisok(var f:puzel);
4. Удаляет первый элемент списка, возвращает ссылку на удаленный элемент
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
procedure DelFirstElement(var f,a:puzel);
5. Удаление элемента из списка после элемента, на который ссылается указатель old
procedure DelElement(var old, a:puzel);
6. Удаление всего списка
8. Восстановление списка из типизированного файла
procedure SpisocFromFile(var f:puzel);
Реализация некоторых процедур.
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
. Вставка a в список после элемента, на который ссылается указатель old.
procedure AddAfter(var old:puzel; a:puzel);
begin^.next:=old^.next; //связываем новый узел с элементом следующим за old^.next:=a; //связываем элемент old с элементом а
if a^.next <> nil then a^.next^.pred:=a; //связывается элемент следующий за old с элементом а(вставляем. эл.)
a^.pred:=old; //связывается с old
. Сохранение списка в типизированный файл
procedure SaveToTipFile(var f:PUzel);
//файл должен быть открыт и связан с файловой переменной
var fz: Fzap1; //файловая переменная
fp:puzel; //текущий указатель, используется для прохождения списка
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
//цикл, запоминающий содержимое каждого узла в типизированном файле
write(fz,fp^.x); //ссылка на типизированный файл
//разименовываем p и вводим x
fp:=fp^.next; //переводим p на следующий элемент списка
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
end;(fz); //закрываем файл;
3. Удаляет первый элемент списка, возвращает ссылку на удаленный элемент
procedure DelFirstElement(var f,a:puzel);
begin:=f; //узел а ссылается на f:=f^.next; //f ссылается на следующий элемент
if f <> nil then f^.pred:=nil;
end;
ГЛАВА 2. Интерфейсная часть проекта
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
В данной курсовой работе используется следующий интерфейс:
В данной работе применяется компонент MainMenu без использования иконок для операций. Для вывода данных используется Memo поле.
1. Компонент MainMenu[4]
Компонент Delphi MainMenu предназначен для добавления к программе главного меню, элемента, без которого не обходится ни одно из приложений для Windows.
Чтобы добавить к программе Delphi главное меню, нужно расместить на Форме в произвольном месте компонент MainMenu.
Компонент MainMenu невизуальный, то есть, хотя и отображается на прототипе Формы как небольшой квадрат, в работающей программе не будет виден. Опции главного меню создаются с помощью специального редактора. Редактор меню вызывается с помощью двойного щелчка по компоненту MainMenu. Первоначально меню пустое, но имеет один выделенный элемент:
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
Для создания первой опции (как правило, опция главного меню программы Файл) нужно перейти в Инспектор объектов и свойству Caption присвоить нужное название.
автоматически создает следующий пустой пункт меню верхнего уровня. А щёлкнув в редакторе меню по синему прямоугольнику Файл, мы сразу получим пустой пункт меню второго уровня. Они не выделены и отображаются белыми прямоугольниками. Щелкнув по одному из них, мы получим возможность редактировать его совершенно аналогично. Пока их свойства не заданы, в работающей программе на форме они не появятся, и удалять в редакторе их не нужно.
Для того чтобы меню выполняло свои функции нужно описать обработчик события OnClick каждого пункта меню. Делается это совершенно стандартным способом. Опишем пункт меню, который будет закрывать программу.
2. Компонент Memo[5]
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
3. Функция InputBox[6]
Функция InputBox отображает простое диалоговое окно с заданным Заголовком (Caption) и Подсказкой (Prompt). Оно просит, чтобы пользователь ввел данные в текстовое поле на диалоговом окне. Значение по умолчанию отображено в текстовом поле.
Если пользователь нажал ОК, значение по умолчанию или данные введенные пользователем, сохраняются в возвращаемой строке, иначе возвращается пустая строка.
Если пользователь отменяет диалог, то возвращаемым значением будет пустая строка.
4. Процедура ShowMessage[7]
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
Процедура ShowMessage выводит простое диалоговое окно на экране с кнопкой ОК, содержащее строку Text.
5. Функция StrToInt[8]
Она поддерживает положительные и отрицательные значения, шестнадцатеричные числа с префиксов $ или 0x.
6. Компонент Image[9]
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
Компонент Delphi Image предназначен для отображения на форме графических изображений. После размещения на форме компонента Image, он принимает вид выделенной прямоугольной области.
Загрузить изображение можно с помощью Object Inspector. Для этого находим свойство Picture и слева от него нажимаем на три точки. Открывается окно Picture Editor и в нем выбираем загрузить(Load), в раскрывшемся окне выбираем файл изображения.
В данной программе изображения используются как кнопки.
Чтобы назначить изображению действие необходимо перейти на вкладку Инспектора объектов Events и выбрать одну из процедур в меню OnClick.
7. Компонент Label[10]
Компонент Label предназначен для отображения статического текста, то есть надписей и меток на Форме, которые не меняются в течение всего времени работы программы. Конечно, текст надписи, отображаемый компонентом Label можно изменить, но не непосредственно, а только программно.
Свойством компонента Label, которое используется для задания отображаемого текста, является Caption (Заголовок англ.).
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
В данной программе компонент Label используется для отображения названия программы в правом верхнем углу.
Подробное описание связки обработчика событий с компонентами MainMenu.
Некоторые обработчики событий для элементов MainMenu, используемые в данной программе:
procedure TForm1.DelSpis1Click(Sender: TObject);(fz);.Clear;
. Восстановление из типизированного файла (.dat):
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
TForm1.SpisocFromFile1Click(Sender: TObject);: puzel;(fz);
. Сохранение в текстовый файл (.txt):
4. Выход из программы:
begin;;
Руководство пользователя
Для начала работы с базой данных её необходимо создать или открыть существующую. Для создания базы данных необходимо зайти в меню Список и выбрать пункт Новый список.
В появившемся диалоговом окне введите данные.
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
Для того чтобы просмотреть базу данных необходимо зайти в меню
Список и выбрать пункт Посмотреть список.
Для того чтобы удалить базу данных необходимо зайти в меню Список и выбрать пункт Удалить список.
В появившемся диалоговом окне введите имя файла с расширением .dat.
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
В появившемся диалоговом окне нужно ввести имя отчета с расширением .txt.
Для упрощения «диалога» с пользователем все пункты меню дублируются кнопками рядом с Memo полем.
ЗАКЛЮЧЕНИЕ
Была создана элементарная базы данных, которая позволяет выполнить следующие действия:
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
. Сохранение списка в типизированном файле
. Восстановление списка из типизированного файла
. Сохранение содержимого поля в текстовый файл
. Восстановление из текстового файла
Программа тестировалась в течение нескольких часов. Сбоев и ошибок при работе программы обнаружено не было.
В связи с нехваткой времени не были осуществлены следующие функции:
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
. Сортировка данных в списке по определенным параметрам, например, расстановка фамилий по алфавиту
. Поиск по критериям
. Встроенное в программу руководство пользователя. Взамен пользователю предлагается использовать руководство, предлагающееся к данной работе.
ПРИЛОЖЕНИЕ 1
.Initialize запускает инициализацию приложения. Убирать не рекомендуется.
Здесь везде используется объект Application. Этот объект всегда существует в ваших программах в единственном экземпляре и создается с помощью строки Application.Initialize. Он происходит от класса TApplication и реализует основные функции управления приложением.
ПРИЛОЖЕНИЕ 2
usp;SysUtils,Dialogs; //тип данных, сохраняемых в узле списка
fam,name,otch:string[20]; //фамилия, имя, отчество
nomer:integer; //номер трудовой книжки
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
data1,data2:string[20]; //дата поступления на работу, дата увольнения
poosh,nakaz,reason:string[40]; //поощрения, наказания, причина увольнения
puzel=^tuzel; //описание типа указатель
tuzel=record //тип узла двусвязного списка
x:zp; //поле, хранящее данные узла
next:puzel; //указатель на следующий узел списка
pred:puzel; //указатель на предыдущий узел списка
Нужна помощь в написании курсовой?
Мы - биржа профессиональных авторов (преподавателей и доцентов вузов). Наша система гарантирует сдачу работы к сроку без плагиата. Правки вносим бесплатно.
Более характерным для Pascal являются типизированные файлы, или файлы произвольного доступа. Основным свойством этих файлов является то, что их структура данных представляет собой последовательность компонентов одного типа. Описывают подобный файл словосочетанием file of с последующим указанием типа компонентов файла, число которых (длина файла) не фиксируется:
var имя_файла : file of тип_компонентов
Поскольку известен тип элементов файла, а следовательно, и объем памяти, отводимой под каждый из них, можно рассчитать позицию каждого из элементов внутри файла. Это позволяет организовать непосредственный доступ к любому элементу типизированного файла. Так, например, рассмотрим описание:
В этом описании указано, что элементами файла являются данные типа Integer , занимающие 2 байта (или 4?). При этом отпадает необходимость в специальном разделении элементов файла, как это делалось в текстовых файлах. Также возможен произвольный доступ к элементам данных (этим типизированный файл несколько напоминает одномерный массив).
Чтобы можно было работать с типизированным файлом, необходимо, как и для текстовых файлов, сначала связать имя файловой переменной с внешним именем файла (оператор Assign). Затем нужно открыть его (используются операторы Reset и Rewrite, но не Append). Операторы Reset и Rewrite открывают файл и для чтения, и для записи (а не только для чтения или только для записи, как при использовании текстовых файлов). Отличие их в том, что оператор Reset открывает только существующий файл (если такого файла нет, будет сгенерирована ошибка времени выполнения). С другой стороны, оператор Rewrite создает новый файл (если файл с таким именем уже имеется, то он будет уничтожен и создан заново). При открытии файла с ним связывается текущий указатель файла, который позиционируется на его первый элемент. Оперировать можно только тем элементом файла, на который ссылается указатель файла. При чтении или записи элемента файла происходит автоматическое перемещение указателя на следующий элемент. Чтение из типизированного файла производится оператором Read (но не ReadLn), а запись в него — оператором Write (но не WriteLn). Однако следует помнить, что в списке вывода оператора Write могут быть только переменные. Типы элементов файла и типы переменных в списках ввода-вывода должны быть согласуемы по присваиванию. Элементами типизированных файлов могут быть числовые, символьные, булевы, строковые значения, массивы, записи, но не файлы или структуры с файловыми элементами.
Узнать количество элементов типизированного файла (размер файла) можно с помощью функции FileSize , для которой используется следующий синтаксис:
Например, если переменная k имеет тип LongInt, а f – файловая переменная типизированного файла, то оператор k := FileSize(f), записывает в переменную k размер файла f.
Элементы типизированного файла нумеруются с нуля (порядковый номер последнего элемента файла на единицу меньше размера файла). Чтобы узнать, на каком элементе располагается указатель файла, используют функцию FilePos:
Текущим положением указателя можно управлять, для чего служит процедура Seek , которая использует следующий синтаксис:
Второй параметр (тип LongInt ) задает номер элемента (отсчет от 0), на который должен переместиться указатель файла. Рассмотрим несколько примеров.
Перейти к пятому (фактически шестому) элементу файла f :
Перейти к предыдущему элементу:
Перейти в конец файла:
Как и для текстовых файлов, можно использовать функцию Eof(имя_файла), которая возвращает значение True, если текущий указатель расположен на признаке конца файла (т. е. при выполнения равенства FilePos(имя_файла) = FileSize(имя_файла)).
Процедура Seek и функция FilePos и FileSize позволяют легко осуществлять коррекцию элементов типизированного файла, имя которого указано в качестве е параметра, начиная с элемента, на котором расположен указатель. Однако уничтожить элемент внутри файла нельзя, для этого файл должен быть перезаписан.
Текстовые файлы могут быть созданы текстовым редактором. Однако типизированные файлы создаются в результате работы какой-либо программы.
Пример записи данных в типизированный файл:
Пример последовательного доступа к типизированному файлу:
В приведенной программе типизированный файл обрабатывается и как файл последовательного доступа, и как файл произвольного доступа.
1. Создание базы данных
В качестве СУБД выберем программу Microsoft Access. Но вы можете использовать и другую базу данных.
Создаем базу данных в Microsoft Access и называем её «test». В этой базе данных создаем таблицу «contacts»:
Заполните базу данных данными:
Базу данных вы можете и другую создать и данные в ней можете другие добавить т.к. это не принципиально, но мы будем работать с данной таблицей.
2. Создание проекта
В Visual Studio создаём проект:
Открываем список «Выберите источник данных» и нажимаем на ссылку «Добавить источник данных проекта»:
Откроется «Мастер настройки источника данных». В качестве источника выбираем «База данных» и идем далее:
В следующем окне идем далее:
В окне «Выбор подключения к базе данных» нажимаем на «Создать подключение»:
В следующем окне «Добавить подключение» выбираем в «Поставщик OLE DB» Microsoft Office 16.0 Access Database. В «Имя сервера или файла» вписываем путь к нашему файлу базы данных Access (который создали в первом пункте). Проверяем подключение и жмем «ОК»:
Update 2021: Visual Studio 2019 требует файл Access старого формата (.mdb – это старый формат баз данных Access 2002-2003):
В следующем окне идем далее:
Выйдет окно для переноса файла базы данных в Ваш проект нажимаем «Да»:
В следующем окне ничего не меняем и идем далее:
В следующем окне выбираем нашу таблицу «contacts» и жмем «Готово»:
После всех манипуляций наша форма должна быть примерно такой:
Если запустить наш проект, то можно увидать данные из нашей базы данных:
Поменяйте ширину всех столбцов на «150» чтобы столбцы вместились во все окно:
Вот так без единой строчки кода мы вывели данные из нашей таблицы на нужную нам форму. Переходим к 3 пункту.
3. Сохранение данных
Базу данных мы видим, но редактировать её невозможно и именно поэтому нам нужна кнопка «Сохранить». Добавляем на форму кнопку и в свойствах меняем имя на «SaveButton». После добавления кнопки нажимаем на нее два раза и пишем код:
После добавления кода запустите свое приложение и попробуйте изменить любую запись и сохранить.
4. Изменение заголовков столбцов таблицы
Изменим текст столбцов и скроем столбец «img»:
Полезные свойства для изменения столбцов:
- HeaderText – задает название столбца.
- Visible – скрывает заголовок или наоборот.
- Width – ширина столбца.
- Resizable – можно ли изменять ширину столбца.
После того как скроете заголовок «img» измените ширину всех остальных заголовков:
5. Защита от случайного удаления
Чтобы пользователь случайно не удалил запись в нашей базе данных нужно сделать так чтобы перед удалением приложение спрашивало об удалении записи.
Для этого перейдем к компоненту DataGridView, откроем список событий и установим обработчик для события UserDeletingRow. Нажмите два раза на пустую строчку возле события и перейдете к коду. Вписываем код:
После этого проверим работоспособность данного кода. Запустим приложение и попробуем удалить запись. Должно быть вот так:
6. Добавление данных
Добавляем новую форму в проект и называем ее «AddForm»:
Изменяем заголовок формы и добавляем на форму компоненты:
- 1 groupBox c текстом «Добавление записи»;
- 4 TextBox и меняем свойство «Name» на (tbName, tbPhone, tbMail, tbPhoto);
- 4 label и меняем текст на (Имя, Телефон, E-mail, Фото);
- 1 Button и меняем свойство «Name» на «AddBtn» (Добавить);
- 1 Button и меняем свойство «Name» на «CloseBtn» (Закрыть).
Переходим на главную форму и добавляем кнопку «Добавить» для добавления новой записи. Дважды щелкаем на нее и пишем код для вызова нашей новой формы:
Проверьте работоспособность приложения:
Возвращаемся на форму «Добавления записи». Пишем обработчик событий для кнопки «CloseBtn»:
Чтобы обработчик событий на кнопке «AddBtn» работал как надо нужно выполнить несколько действий. Зайти в «Form1.Designer.cs» и изменить модификаторы доступа на «public» нижеуказанных файлов:
- System.Windows.Forms.DataGridView dataGridView1;
- testDataSet testDataSet;
- System.Windows.Forms.BindingSource contactsBindingSource;
- testDataSetTableAdapters.contactsTableAdapter contactsTableAdapter;
Это нужно для того чтобы наша база данных была доступна для разных с ней манипуляций во всех создаваемых нами формах. Обработчик событий для кнопки «AddBtn»:
Проверьте работу приложения. Добавьте несколько новых записей через новую форму добавления записей.
7. Поиск данных
Реализуем самый простой поиск. Данный поиск используется в небольших приложениях. Создаем новую форму с именем «SearchForm» и добавляем на форму компоненты:
- 1 GroupBox (Поиск);
- 1 label;
- 2 button (Найти и Закрыть);
- 1 TextBox и меняем в свойствах «Name» на «tbStr».
Переходим на главную форму и добавляем кнопку «Найти» для поиска записей. Дважды щелкаем на нее и пишем код для вызова нашей новой формы:
Проверяем работоспособность приложения:
Возвращаемся на форму «Поиска». Пишем обработчик событий для кнопки «Закрыть»:
Пишем обработчик событий для кнопки «Найти»:
На главную форму добавляем кнопку «Выход» и пишем для нее код:
Итоговая форма приложения:
Своя СУБД за 3 недели. Нужно всего-лишь каждый день немного времени уделять архитектуре; и всё остальное время вкалывать на результат, печатая и перепечатывая сотни строк кода.
По закону Мерфи, если есть более одного проекта на выбор — я возьмусь за самый сложный из предложенных. Так случилось и с последним заданием курса о системах управления базами данных (СУБД).
Постановка задачи
Согласно ТЗ, требовалось создать СУБД с нуля на Vanilla Python 3.6 (без сторонних библиотек). Конечный продукт должен обладать следующими свойствами:
- хранит базу в бинарном формате в едином файле
- DDL: поддерживает три типа данных: Integer, Float и Varchar(N). Для упрощения, все они фиксированной длины.
- DML: поддерживает базовые SQL операции:
- INSERT
- UPDATE
- DELETE
- SELECT с WHERE и JOIN. С каким именно JOIN — указано не было, поэтому на всякий случай мы сделали и CROSS, и INNER
Подход к проектированию
Разработать СУБД с нуля казалось нетривиальной задачей (как ни странно, так оно и оказалось). Поэтому мы — ecat3 и @ratijas — подошли к этому делу научно. В команде нас только двое (не считая себя и мою персону), а значит распиливать задачи и координировать их выполнение в разы легче, чем, собственно, их выполнять. По окончании распила вышло следующе:
Задача Исполнитель(-и) Парсер + AST + REPL ratijas, написавший не один калькулятор на всяких lex/yacc Структура файла базы ecat3, съевший собаку на файловых системах Движок
(низкоуровневые операции над базой)ecat3 Интерфейс
(высокоуровневая склейка)Вместе
Изначально времени было выделено две недели, поэтому предполагалось выполнить индивидуальные задачи за неделю, а оставшееся время совместно уделить склейке и тестированию.Так родилась идея названия: dropSQL. Собственно /drop'ом называется отчисление студента из университета; по некоторым причинам, это слово получило широкое распространение у нас в Иннополисе. (Ещё один местный феномен: аутизм, или, более корректно, /autism. Но мы приберегли его на особый случай.)
Первым делом разложили грамматику dropSQL на BNF (и зря — левые рекурсии не подходят нисходящим парсерам).
Примеры валидных dropSQL команд, из онлайн справки продуктаРаботаем на результат! Никаких исключений!
После нескольких месяцев с Rust в качестве основного языка, крайне не хотелось снова погрязнуть в обработке исключений. Очевидным аргументом против исключений является то, что разбрасываться ими дорого, а ловить — громоздко. Ситуация ухудшается тем, что Python даже в версии 3.6 с Type Annotations, в отличие от той же Java, не позволяет указать типы исключений, которые могут вылететь из метода. Иначе говоря: глядя на сигнатуру метода, должно стать ясно, чего от него ожидать. Так почему бы не объеденить эти типы под одной крышей, которая называется «enum Error»? А над этим создать ещё одну «крышу» под названием Result; о нём пойдёт речь чуть ниже. Конечно, в стандартной библиотеке есть места, которые могут «взорваться»; но такие вызовы в нашем коде надежно обложены try'ями со всех сторон, которые сводят любое исключение к возврату ошибки, минимизируя возникновение внештатных ситуаций во время исполнения.
Итак, было решено навелосипедить алгебраический тип результата (привет, Rust). В питоне с алгебраическими типами всё плохо; а модуль стандартной библиотеки enum больше напоминает чистую сишечку.
В худших традициях ООП, используем наследование для определения конструкторов результата: Ok и Err. И не забываем про статическую типизацию (Ну мааам, у меня уже третья версия! Можно у меня будет наконец статическая типизация в Python, ну пожаалуйста?):
Отлично! Приправим немного соусом из функциональных преобразований. Далеко не все понадобятся, так что возьмем необходимый минимум.
И сразу пример использования:
Писанина в таком стиле всё ещё занимает по 3 строчки на каждый вызов. Но зато приходит четкое понимание, где какие ошибки могли возникнуть, и что с ними произойдет. Плюс, код продолжается на том же уровне вложенности; это весьма важное качество, если метод содержит полдюжины однородных вызовов.
Парсер, IResult, Error::, REPL, 9600 бод и все-все-все
Парсер занял значительную часть времени разработки. Грамотно составленный парсер должен обеспечить пользователю удобство использования фронтэнда продукта. Важнейшими задачами парсера являются:
- Внятные репорты об ошибках
- Интерактивный инкрементальный построчный ввод, или, проще говоря, REPL
Один из первых вопросов, требовавших ответа: как быть с ошибками? Как быть — выше уже разобрались. Но что вообще представляет собой ошибка? Например, после "/create table" может находиться «if not exists», а может и не находиться — ошибка ли это? Если да, то какого рода? где должна быть обработана? («Поставьте на паузу», и предложите свои варианты в комментариях.)
Первая версия парсера различала два вида ошибок: Expected (ожидали одно, а получили что-то другое) и EOF (конец файла) как подкласс предыдущего. Всё бы ничего, пока дело не дошло до REPL. Такой парсер не различает между частиным вводом (началом команды) и отсутствием чего-либо (EOF, если так можно сказать про интерактивный ввод с терминала). Только неделю спустя, методом проб и ошибок удалось найти схему, удовлетворяющую нашей доменной области.
Схема состоит в том, что всё есть поток, а парсер — лишь скромный метод next() у потока. А также в том, что класс ошибки должен быть переписан на алгебраический (подобно Result), и вместо EOF введены варианты Empty и Incomplete.
Потоки организованы в иерархию. Каждый уровень содержит свой буфер, что позволяет заглядывать вперёд (peek() -> IResult[T]) и откатываться назад (back(n: int = 1) -> None) при необходимости.
А самое приятное, что поток можно "собрать" в один большой список всех IOk(элементов), что выдает next() — до первой IErr(), разумеется. При чем список вернется лишь когда IErr содержала Empty; в противном случае, ошибка пробросится выше. Эта конструкция позволяет легко и элегантно построить REPL.
Characters
Этот поток проходит по символам строки. Единственный тип ошибки: Empty в конце строки.
Tokens
Поток токенов. Его второе имя — Лексер. Тут встречаются и ошибки, и строки без закрывающих кавычек, и всякое…
Каждый тип токенов, включая каждое ключевое слово по отдельности, представлен отдельным классом-вариантом абстрактного класса Token (или лучше думать про него как про enum Token?) Это для того, чтобы парсеру команд (statements) было удобно кастовать токены к конкретным типам.
Statements
Кульминация, парсер собственной персоной. Вместо тысячи слов, пару сниппетов:
Важно отметить, что любые ошибки типа Empty, кроме самого первого, парсеры должны преобразовывать в Incomplete, для корректной работы REPL. Для этого есть вспомагательная функция empty_to_incomplete() -> Error. Чего нет, и никогда не будет, так это макросов: строка if not t: return IErr(t.err().empty_to_incomplete()) встречается в кодовой базе по меньшей мере 50 раз, и тут ничего не попишешь. Серъёзно, в какой-то момент захотелось юзать Сишный препроцессор.
Про бинарный формат
Глобально файл базы поделен на блоки, и размер файла кратен размеру блока. Размер блока по умолчанию равен 12 КиБ, но опционально может быть увеличен до 18, 24 или 36 КиБ. Если Вы дата сатанист на магистратуре, и у вас очень большие данные — можно поднять даже до 42 КиБ.
Метаданных базы на текущий момент не так много: название длиной до 256 байт и кол-во блоков с данными.
Мета-блок таблицы самый непростой. Тут хранится название таблицы, список всех колонок с их типами, количество записей и указатели на блоки данных.
Количество таблиц фиксировано в коде. Хотя это относительно легко может быть исправлено, если хранить указатели на мета-блоки таблиц в мета-блоке базы.
Указатели на блоки работают по принципу указателей в inode. Об этом прекрасно писал Таненбаум и дюжины других уважаемых людей. Таким образом, таблицы видят свои блоки с данными как «страницы». Разница в том, что страницы, идущие с точки зрения таблицы по порядку, физически располагаются в файле как бог на душу положит. Проводя аналогии с virtual memory в операционках, страница: virtual page number, блок: physical page number.
Блоки данных не имеют конкретной структуры сами по себе. Но когда их объеденить в порядке, продиктованном указателями, они предстанут непрерывным потоком записей (record / tuple) фиксированной длины. Таким образом, зная порядковый номер записи, извлечь или записать её — операция практически константного времени, O(1 * ), с амортизацией на выделение новых блоков при необходимости.
Первый байт записи содержит информацию о том, жива ли эта запись, или была удалена. Остальные работу по упаковке и распаковке данных берет на себя стандартный модуль struct.
Операция /update всегда делает перезапись «in-place», а /delete только подменяет первый байт. Операция VACUUM не поддерживается.
Про операции над таблицами, RowSet и джойны
Пришло время прочно скрепить лучшее из двух миров несколькими мотками скотча.
MC слева — AST узлы верхнего уровня (подклассы AstStmt). Их выполнение происходит в контексте соединения с базой. Также поддерживаются позиционные аргументы — "?N" токены в выражениях в теле запроса, например: "/delete from student /where name = ?1 /drop". Сами выражения рекурсивны, а их вычисление не представляет собой научного прорыва.
MC справа — драйвер БД, оперирующий над записями по одной за раз, используя порядковый номер внутри таблицы как идентификатор. Только он знает, какие таблицы существуют, и как с ними работать.
Их первый совместный сингл про креативность. Создать новую таблицу не сложнее, чем взять первый попавшийся пустой дескриптор из 16, и вписать туда название и список колонок.
Затем, трек с символическим названием /drop. В домашней версии демо-записи происходит следующее: 1) найти дескриптор таблицы по имени; 2) обнулить. Кого волнуют неосвобожденные блоки со страницами данных?
Insert снисходителен к нарушению порядка колонок, поэтому перед отправкой кортежа на запись, его пропускают через специальный фильтр transition_vector.
Далее речь пойдет про работу с записями таблиц, поэтому позвольте сразу представить нашего рефери: RowSet.
Главный конкретный подвид этого зверя — TableRowSet — занимается выборкой всех живых (не удаленных) записей по порядку. Прочие разновидности RowSet в dropSQL реализуют необходимый минимум реляционной алгебры.
Оператор реляционной алгебры Обозначение Класс Проекция π(ID, NAME) expr Переименование ρa/b expr Выборка σ(PRICE>90) expr Декартово произведение PRODUCTS × SELLERS Inner Join
(назовём это расширением)σ(cond)( A x B )
Кроме того ещё есть программируемый MockRowSet. Он хорош для тестирования. Также, с его помощью возможен доступ к мастер-таблице под названием "/autism".Прелесть в том, что различные RowSet'ы можно комбинировать как угодно: выбрать таблицу «student», указать алиас «S», отфильтровать «/where scholarship > '12k'», выбрать другую таблицу «courses», соединить «/on (course/sid = S/id) /and (course/grade < 'B')», проецировать на «S/id, S/first_name/as/name» — это представляется следующей иерархией:
Итак, вооружившись столь мощным инструментом, возвращаемся к лютневой музыке XVI века…Про четвертый трек, /select, тут больше нечего добавить. Симфония из RowSet'ов такая, что душу завораживает. Благодаря этому, реализация получилась крайне лаконичной.
Два последних: /update и /delete используют наработки предшественника. При чем /update применяет технику, похожую на описанный выше «transition_vector».
Читайте также: