Как стать автором
Обновить

Как я вуз автоматизировал

Уровень сложностиПростой
Время на прочтение17 мин
Количество просмотров5.3K

Здравствуйте.

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

Хочу рассказать об одной самописной системе, которую мы используем уже очень давно. И о ее развитии (в другой статье).

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

Довольно давно (не пишу сколько, так как самому страшно)) взялся я за автоматизацию нашей организации. А именно автоматизацию учебного процесса. В те времена еще было популярно написание программ для Windows. По этому для реализации данного проекта был выбран Delphi седьмой версии. В качестве базы данных был выбран Firebird 2.5. Кстати, так и живу я этом сервере этой же версии. Последнее время были порывы в сторону PostgreSQL - отказался ввиду нецелесообразности. Так как в принципе никого не интересует на какой базе работает решение. Работает и ладно.  В качестве библиотеки доступа к серверу был выбран FIBPlus. Не знаю жива ли эта библиотека, но тогда это была наиболее удобная для использования.

В качестве софта по работе с базой был выбран IBExpert. С моей точки зрения это наиболее удобный инструмент. Ничего лишнего, все в пределах разумного.  На  тот момент были особые условия использования для граждан из бывшего СССР. Читай - бесплатно и без ограничений. Правда в связи с последними событиями это условие перестало работать. Но старые версии вполне рабочие и покрывают все необходимые задачи работы с базой.

Задача была поставлена пространно. Если мне не изменяет память - надо б автоматизировать. Что , как особо не уточнялось.  Позже было уточнено - надо вести учет посещаемости. Уточнял постановку задачи в ныне модных терминах product owner.

Так как требовалось автоматизировать учет посещаемости, то взгляд на систему с точки зрения product owner был такой - "Учитывает посещаемость? ну и хорошо". Как будут устроены смежные блоки системы - не особо интересовало.

Посидел я тогда, покумекал. По всему получалось "учет посещаемости" это , мягко говоря, узкая постановка задачи. Вернее это ее конечный результат.

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

Что такое Win программа в контексте решения поставленной задачи. Если совсем утрировать, то это набор форм и некая логика их работы. Поэтому сначала я занялся формошлепством. Но со временем формы начали множиться как кролики ) По этому ввел понятие метаданных. Которые описывают все существующие в системе объекты. Это справочники, отчеты, документы, селекторы и тд. Описание метаданных хранится в двух таблицах. Первая описывает объекты метаданных с их свойствами, вторая - те свойства метаданных, которые можно охарактеризовать как табличные. Например описание полей справочника, документа или отчета.  Связь один ко многим. Назовем эти таблицы таблицей шапки и таблицей  тела описания метаданных.

Каждый объект на фронте обслуживает отдельный класс. Он ответственен за логику работы и отображения объекта.

Метаданные описывают следующие сущности.

1. Справочник

В свойствах справочник указана кроме всего прочего dll , которую надо загрузить,  класс объекта формы справочника. И источник данных - хранимая процедура  с определенным шаблоном имени. Префикс плюс ид справочника.  Для справочников использовал именно хранимку на случай, если вдруг захочется рефакторинга базы. В табличной части описания объекта метаданных перечислены все поля, которые возвращает хранимая процедура, которая обеспечивает выборку данных из таблиц справочника.  И небольшое количество кастомных параметров. Например видимость, заголовок и тд. Все эти данные используются как для отображения справочника как такого (в отдельной форме), так и для отображения данных справочника в прочих частях системы (например в combobox). Заполняется табличная часть описания этого объекта просто автоматической генерацией таким вот запросом

select RDB$PROCEDURE_PARAMETERS.rdb$parameter_name,RDB$PROCEDURE_PARAMETERS.rdb$parameter_number from RDB$PROCEDURE_PARAMETERS where RDB$PROCEDURE_NAME = trim(upper('SP_NAME'))  and RDB$PARAMETER_TYPE = 1

Получаем все выходные параметры процедуры и складываем их названия в табличку описания параметров объекта метаданных. Расставляем значения visibility, width и прочие. И вуаля - справочник готов. Его ИД можно повесить в описании документов на нужное поле и он автоматически будет там использоваться. Отображаться либо в combobox либо отдельном окне. Последнее редкость

2. Селекторы

Назвал я их так по той причине , что они являются формой выбора каких либо данных. На клиенте это отдельный класс, которые отображает данные этого самого селектора в гриде. Грид строится на основании метаданных. Описание метаданных такое же, как и в случае справочника. Ну и плюс некоторые специфические параметры. Один из параметров - режим работы селектора. Вернее режим, в котором будет работать клик при выборе элемента селектора. Режимов два. Либо при клике закрыть форму селектора. Либо оставить открытой. Последний режим используется, например, при заполнении тела документа. Когда в тело надо "накликать" несколько позиций.

Источник данных для селектора формируется с помощью хранимой процедуры вида show_mode_Х. Описание полей селектора в метаданных формируется примерно так же , как описания полей справочника - генерируются служебной хранимкой на основе метаданных Firebird.

У хранимки для отображения селектора есть входные параметры. Условно разделил эти параметры на два типа. Это базовые параметры(контекстные). Например ИД факультета. Он берется из свойств пользователя. Или, например,  дата. Она берется из шапки документа.  Единственный нюанс - имя параметра , в частности дата, должны совпадать с именем поля датасета шапки документа.  Соответственно при отображении селектора на фронте часть параметров , как уже говорилось, берутся из свойств пользователя. Прочие параметры из датасета шапки документа. Если поле, которое используется в качестве параметра селектора еще не заполнено - пользователю сообщается, что надо заполнить в шапке, чтобы селектор открылся. Самое интересное, что это моих пользователей сильно сбивает с толку ) Не заполнена дата! Какая дата??

Селектор тесно связаны с документами. Он может быть привязан в метаданных либо к полю шапки документа, либо к полю  телу. При открытии селектора просто осуществляется  поиск совпадений полей селектора и полей документа по имени поля  и в параметры подставляются соответствующие данные. За исключением служебных полей.  Копируются либо в датасет шапки либо в датасет тела документа. Смотря к чему селектор привязан в описании метаданных документа. Единственно, что если селектор работает в связке с телом документа, то перед добавлением делается insert в соответствующий датасет.

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

 3. Документ

Он собственно описывает  параметры конкретного документа.  Если представить параметры документа в виде js объекта, то выглядеть это будет примерно так

var header = {

Id:number,

title:string,

frameName:string,

oneClickField:string,

haveBody:boolean,

headerSelectorId:number,

...

}

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

Шапки документов на клиенте реализованы в виде TFrame. В описании документа в метаданных есть соответствующий параметр frameName. И плюс немного магии-инициализации.

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

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

4. Журналы и отчеты

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

На фронте за работу журнала и отчета отвечают соответственно два класса. В случае отчета на клиенте отрисовывается, кроме грида отчета, блок выбора параметров. Он не такой динамично формируемый, как хотелось бы. Набор параметров формируется на основе параметров хранимой процедуры (описанных в метаданных), который получился довольно небольшим. Это те же самые контекстные переменные пользователя , плюс пара вариаций на тему даты (просто дата, выбор диапазона). Есть пользователи, у которых нет определенных контекстных переменных, но в параметрах отчета они присутствуют. Например у пользователя с типом Деканат есть контекстная переменная Факультет. А у пользователя Учебный отдел нет такой контекстной переменной. Однако и тот и тот пользователь может использовать отчет с параметром Faculty_id. Просто у первого пользователя в форме отчета он будет выбран и заблокирован на изменение, а у второго пользователя будет возможность самостоятельно его выбрать.

Некоторые типы журналов и документов являются уникальными. Те они не описываются в метаданных как те, что были описаны выше.  Например учебные планы,  форма ввода пропусков (которые по уважительной причине, о них ниже). Для них созданы на фронте отдельные классы, которые обслуживают эти объекты. И для них я выделил отдельный тип объекта метаданных - сервисный.  В его описании указывается из какой DLL брать какой класс формы и на фронте он просто запускается. Все логика работы зашита в соответствующем классе.

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

  1. Создать таблицы для хранения документа. Мастер-деталь

  2. Создать для них вьюхи и триггера с логикой, если она необходима для конкретного документа.

  3. Создать хранимую процедуру журнала

  4. Настроить объекты метаданных

Все. В системе появляется еще один документ со своим журналом!

Несколько слов об архитектуре системы и базы данных

Сначала думал нарисовать схему базы. Но потом решил, что она будет слишком громоздка.

Все база построена на принципе , который называется Документо-ориентированный подход. Если по простому - большинство сущностей базы отражают реальные бумажные документы. Ну или почти ) А документ в базе представлен на уровне таблиц обыкновенной мастер-деталью.

Как выше я говорил - на проблему учета посещаемости посмотрели довольно узко. Если же посмотреть более широко, то вырисовываются такие блоки.

Cправочники. Естественно, куда без них. Посмотрел сейчас базу - аж 110 штук. Есть небольшая группа "горячих" справочников . Это которые используются в основной массе таблиц. Остальные справочники редко используются.

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

Собственно как физлицу стать абитуриентом и потом студентом? Правильно. Подать документы в приемную комиссию.

Этим заведует блок Абитуриент.

Для того, чтобы простое физлицо стало абитуриентом в системе присутствуют несколько документов. Это документ "Направление подготовки". Он просто декларирует какие направления подготовки реализуются в конкретном учебном году на каком факультете.  Своего рода справочник, но оформленный в виде отдельного документа.

Далее в иерархии стоит документ, который называется "Карточка абитуриента". По сути он объединяет информацию о физлице (хранится в отдельной таблице) и информацию о направлениях подготовки, на которые у этого физлица поданы документы. Мастер-деталь. Если абитуриент подавал заявления на несколько специальностей-направлений, то ,соответственно, в детали документа будет несколько записей.

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

И, собственно , и все . Да, есть еще ведомости вступительных экзаменов, но как то они не прижились. Отмечу, что та таблица, которая отражает данные о физлице, по сути , является частью Карточки абитуриента. Что впрочем и логично.

О движениях студентов

После того, как абитуриент зачислен в ряды студентов оформляется документ, который называется "Приказ о закреплении групп". На счет того, есть ли такой официальный документ в вузе - не уверен. Но он в любом случае есть в виде какой то эксель-таблички. В системе имеется два таких документа. Один ответственен за закрепление  студентов первого курса за группами (бывших абитуриентов). Второй фиксирует закрепление студентов за группами при переводе на следующий год. Оба документа однотипны, хранятся в одних и тех же таблицах.  Единственно их отличие - в шапке документа используются разные документы-основания. В случае с первокурсниками это приказ о зачислении. В случае студентов других курсов - приказ о переводе на следующий год. После выбора документа-основания селектор, который ответственен за добавление данных в табличную часть документа использует ИД документа-основания как параметр и в селекторе отображаются данные из документа основания. Те лишнего не покажет, лишнего не добавит. Механизм работы параметров селектора описан выше.

Несколько слов о структуре хранения движения

Вышеописанные документы движения в базе хранятся в трех таблицах. Назовем их таблицами движения.  Таблица шапки, табличная часть и таблица, которая ссылается на тело, и хранит в себе ссылку на справочник групп. На базе этих трех таблиц реализованы еще несколько документов, которые так или иначе отражают движения студентов. Их оказалось 11 штук.

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

Вся информация о  движении студентов хранится в таблице тела документа движения. Как таковое движение, чтобы иметь возможность определить на конкретную дату где находится (и в каком статусе) студент, хранятся в двух полях деталь-таблицы. Это поля from_date и to_date. Первое поле по сути дублирует (на триггерах) дату документа из шапки (либо имеет иное значение, если в теле документа о движении студента предусмотрена начальная дата для конкретного студента) . Второе вычисляется и фактически всегда равно дате окончания учебного года. Иное значение данное поле может иметь только в документе, который фиксирует  отчисление студента. Если отражается выпуск (в принципе тоже движение), то оба поля будут иметь значение даты документа, который фиксирует выпуск. Если это реальное отчисление - будет стоять 9999 год.

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

В итоге в таблице учета движений мы имеем практически полную информацию о движении студентов. Текущие данные получаются простым select * from movements where :date between from_date and to_date. Чтобы получить данные о группе студента мы дополнительно делаем join  той самой выбившейся из нормальной архитектуры третьей таблицы, в которой хранится ИД справочника группы.  В результирующую выборку попадут так же и отчисленные студенты. Но их мы исключаем из выборки просто по условию and group_id is not null. Может это слегка спорное решение. Но работает ) Чтобы получить список активных студентов конкретной группы можно в этой выборке  применить условие and group_id = 123. Но тут есть небольшой нюанс. Если мы делаем запрос к таблицам движения с условием and group_id = 123, то мы получаем список студентов всего вуза.  А потом мы уже делаем фильтр по группе. В итоге получается слишком много лишних чтений из этих таблиц. Так как сначала из базы читаются все студенты вуза. А потом уже применяется фильтр.  По этому в зависимости от того, в какой части системы мы обращаемся к движениям студентов, делается выборка либо по вышеозначенному условию ( если вдруг надо реально получить всех студентов вуза), либо начинаем раскручивать движения со справочника групп. Те ведущей таблицей является справочник групп с фильтром по искомой группе (ну или сразу по факультету). А ведомой уже блок таблиц движения студентов. Существенно снижает время выборки !

Что еще надо для учета той самой посещаемости? Наверно ведомости этой самой посещаемости.

Раз мы хотим иметь информацию о посещаемости, то нам нужны соответствующие ведомости.

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

Учебные и рабочие планы

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

Хранение учебного плана в базе реализовано с использованием аж четырех таблиц.  Иерархия такова.

Шапка -> набор дисциплин (с различными параметрами) -> расчасовка по семестрам -> формы контроля.

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

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

Просто дисциплина тут все понятно. Например История. Она обязательна для всех.  Тип расширенной дисциплины был введен для порядка и разграничение логики. Есть у нас дисциплины, которые, как я по простому называю, расширяются. Ее название формируется из двух частей. Базовое название + название , отражающее специализацию студента. Но дисциплины данного типа обязательны для изучения для всех специализаций. Третий тип ,дисциплина специализации, тоже составной. Базовое название + специализация. На данный тип дисциплины ходят студенты только конкретной специализации. Если по простому - на дисциплину Базовые Машины (трактора) будут ходить все студенты специальности вне зависимости от специализации. А на дисциплину Машины (трактора) будут ходить только те студенты специальности, которые имеют специализацию Трактора.  На первой дисциплине рассказывают про трактора  в общеобразовательных целях. На второй - более углубленное изучение дисциплины. Есть еще дисциплины, на которые ходят только мальчики или только девочки ) Но такие редко используются. Для этого есть отдельный тип дисциплины. Плюсом расширенные дисциплины и дисциплины специализации ведутся разными преподавателями и на каждой из них висит отдельная форма контроля. По этому и получилась такая архитектура базы в части хранения учебных планов.

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

Итак. На данном этапе в системе мы имеем блоки  Абитуриента, движения студентов, учебные и рабочие планы.

Чтобы учитывать посещаемость, а это была изначальная цель, нам нужна подсистема работы с ведомостями посещения.

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

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

Второй документ - непосредственно текущие ведомости. Их заносят в систему продюсеры данных - кафедры.  На данный момент в таблице посещение в базе лежит 245 тысяч ведомостей) По структуре все просто. Шапка и табличная часть. В шапке есть дата, ссылка на документ , закрепляющий преподавателей за дисциплинами учебных планов. В табличной части студенты в виде ссылки на документ зачисления( тот самый глобальный ИД студента). Табличные части ведомостей генерируются соответствующей хранимой процедурой в момент непосредственного заполнения. Хранимка делает запрос к таблицам движения студентов. Как работает эта выборка было описано выше. Да, тут получается некая неувязочка в плане того, что могут сначала сгенерировать ведомость, а потом внести документ об отчислении студента. Но это не критично. Так как при редактировании ведомости пользователем отображается статус студента и пользователь может решить что с ним делать, ставить ему оценку или нет. Плюсом в различных отчетах эта ситуация обрабатывается в хранимых процедурах отчета.

Модуль учета пропусков

Вроде как мы пришли к цели - у нас имеется более менее структурированная информация о посещениях.  Но как быть с уважительной причиной пропусков?

Для этого есть отдельный модуль. Сейчас пишу эти строки и понимаю на сколько он древний и сколько времени я в него не заглядывал. Но работает. И без особых нареканий.

В данной подсистеме имеется справочник типов пропусков. Типов насчитывается 3.

1. Пропуск типа занятия. Например только лекции.

2. Пропуск конкретной дисциплины. Например История.

3. Пропуск конкретной пары. Например первой.

По структуре таблиц вся та же двухуровневая мастер-деталь. В мастере хранятся ссылки на типы пропусков, ссылка на студента и период дат действия(from, to). В детали прочие параметры уважительного пропуска занятий. Такие как номера пар, типы занятий и тд. В зависимости от типа самого уважительного пропуская.

При вызове хранимой процедуры отчета о посещаемости таблицы данного модуля в запросе объединяются с таблицами уважительных пропусков по условию ИД студента и Vedomost.lesson_date between propusk.from_date and propusk.to_date.  И в запросе в блоке case when end проверяется условие попадания того или иного уважительного пропуска в ведомость посещения.  Условие там трехэтажное, по этому не привожу кода. Но принцип, думаю понятен.

Приведу просто для статистики и наглядности количество пропусков по уважительной причине, которые на данный момент есть в системе. Их уже набралось 40 тысяч !

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

И на этом мы не остановились ;-)

От сессии до сессии живут студенты весело

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

Архитектурно в базе я разделил это на два блока. Это непосредственно таблицы, которые обслуживают хранение данных о всех документах сессии (приказы и непосредственно ведомости). И отдельно выделил такую сущность, как индивидуальный план обучения студента.

Сначала о последнем. Для каждого студента в двух отдельных таблицах хранится информация о его индивидуальном учебном плане. Большинство, кто слышит про данную часть системы восклицает - В России нет индивидуальной системы обучение. Но, по сути, это жонглирование понятиями. Так или иначе у каждого студента есть этот самый индивидуальный учебный план. Только он у всех студентов одной специальности одного года поступления одинаков ). Но он есть. Кстати, до сих пор объясняю людям, что учебные планы разных лет поступления даже если они один в один одинаковы, то они все равно разные. Отличаются как минимум годом начала действия.

И вот в этих двух таблицах (все та мастер-деталь) и хранится данная информация.  В шапке данной связки таблиц храниться ИД студента и ссылка на учебный план. Так же в мастер-таблице хранится год  , в котором  студент попал на данный учебный план. В теле документа, помимо служебной информации хранится ссылка на табличную часть учебного плана. Вернее на ту часть учебного плана, которая отражает форму контроля в разрезе семестров. Заполняется данная связка таблиц в случае внесения в систему документов, которые отражают движения студентов (которые несут под собой изменение учебного плана). Это закрепления групп первого курса (начальная «расстановка»), различные восстановления студентов (академка, как таковые восстановления из отчисленных и тд.). Вызов хранимой процедуры заполнения данных таблиц осуществляется из триггеров базы, которые висят на вьюхах соответствующих документов движения.

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

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

Формируются шапки ведомостей таким образом: так как у нас в приказе о сессии есть набор ссылок на рабочие планы, которые "участвуют" в сессии, то выбираем все группы, которые обучаются на данном плане (у каждой группы есть соответствующий атрибут), получаем все формы контроля и дисциплины, которые присутствуют в данном рабочем плане. И все это записываем в шапку ведомости. Тут может показаться, что данная схема как то выбивается из структуры базы, что сложновато будет создать соответствующие ограничения в таблицах базы. Да, от части соглашусь. Но практика показала, что можно не использовать жесткие ограничения. Все проверки на возможные дубли или еще какие то несоответствия проводятся в хранимой процедур, которая формирует эти данные. И, можно сказать, что данная процедура идемпотентна. Главное руками в таблицу не лезть.

Сама ведомость, ее табличная часть, сущность динамическая. Для ее отображения соответствующая вьюха берет данные из того самого индивидуального плана обучения студента и данные из таблиц движения студентов. В итоге получается динамическая табличная часть ведомости, а данные о результатах экзаменов и зачетов хранятся непосредственно в индивидуальном плане обучения студента. Приводить текст запрос, думаю, нецелесообразно, так как он относительно  большой.  На скорость запросов к базе данная динамика не влияет. Прямо сейчас проверил - формирование занимает 16 ms )

Что-то получилось много текста :-) По этому закругляюсь. Но дальше можно еще рассказывать и рассказывать :-)

Не знаю будет ли это репрезентативно, но приеду небольшую статистику по базе.

34 приказа о сессии. Хм, получается база живет уже минимум 16 лет :-)

5 247 переаттестации студентов. Да, очень часто студенты пересдают зачеты и экзамены :-)

246  000 текущих ведомостей  посещений

19 000 ведомостей сессии

Негоже метиться такими "метриками", как количество объектов метаданных в базе, но все ж приведу. Вдруг кому интересно будет. Около 700 таблиц, 850 вьюх, На них в общей сложности 1800 триггеров, около 2000 хранимых процедур. Но это с учетом прочего функционала системы.

За весь период топ 5 фамилий выглядит так: Иванова, Иванов, Смирнова, Петров, Васильева

Топ 5 имен: Александр, Сергей, Ольга, Анна, Екатерина

Можно еще представить средний балл по сессии. Но, думаю, это будет некорректно.

Спасибо за внимание! В случае положительного отклика сообщества расскажу в отдельной статье как мы пережили пандемию!

Теги:
Хабы:
+14
Комментарии21

Публикации

Работа

Ближайшие события