Исследование системы составления расписания

Первичные и вторичные индексы Paradox§

Исследованы индексы DB/1_sem_2026_2027 чтением заголовков и активных цепочек блоков. Исходные файлы не изменялись. Скрипт research/inspect_indexes.py отдельно разбирает вторичные индексы типа 8, а текущие таблицы читает через существующий tools/paradox_read.py без его изменения. Результаты и контрольные суммы сохранены в research/indexes-evidence.json.

1. Парность первичных индексов§

Имена сопоставлены без учета регистра, как в типичном Windows-окружении исходного приложения. Получено 18 пар:

Таблица Первичный индекс
CHAIRS.DB CHAIRS.PX
Classes.DB Classes.PX
CLASSINF.DB CLASSINF.PX
Courses.DB Courses.PX
DAYS.DB DAYS.PX
FACULTY.DB FACULTY.PX
GROUPS.DB GROUPS.PX
GRPPLAN.DB GRPPLAN.PX
Plan.DB Plan.PX
PLANITEM.DB PLANITEM.PX
PLANROOM.DB PLANROOM.PX
ROOMRSTR.DB ROOMRSTR.PX
Rooms.DB Rooms.PX
STREAMS.DB STREAMS.PX
SUBJECTS.DB SUBJECTS.PX
TCHRRSTR.DB TCHRRSTR.PX
TEACHERS.DB TEACHERS.PX
TIMES.DB TIMES.PX

Исключения: ClassesInf.DB без ClassesInf.PX и LOAD.PX без LOAD.DB. ClassesInf.DB не объявляет первичный ключ, поэтому такая структура допустима. LOAD.PX — оставшийся файл исторического семейства; наличие LOAD.TV и LOAD.FAM со ссылками на CLASSES подтверждает исторические следы, но не доказывает утрату LOAD.DB. Сами .PX — индексы Paradox, не изображения.

2. Все шесть вторичных индексов§

В каждой паре .XG* — упорядоченный набор индексных значений, а .YG* — связанная с ним индексная структура. Заголовки .XG* содержат fileType = 8, versionID = 11 (семейство формата Paradox 5), кодовую страницу 866, блоки по 2 048 байт, заголовок 2 048 байт. У .YG* установлен fileType = 7; структура заголовка отличается, поэтому чтение кодовой страницы по смещению обычного .DB для .YG* ошибочно. Основа разбора — исходный код pxlib, get_px_head(), с проверкой на фактических файлах.

Файлы Внутреннее имя индекса Поля индекса в порядке Первичный ключ исходной таблицы Записей
Classes.XG0 + Classes.YG0 PlanNoIdx PlanNo ClassNo 15 111
Classes.XG1 + Classes.YG1 TeacherNoIdx TeacherNo ClassNo 15 111
GROUPS.XG0 + GROUPS.YG0 GroupStreamIdx StreamNo GroupNo 554
GRPPLAN.XG0 + GRPPLAN.YG0 PlanGroupIdx PlanNo, GroupNo, Subgroup GroupNo, Subgroup, PlanNo 21 419
STREAMS.XG0 + STREAMS.YG0 StreamNameIdx StreamName StreamNo 140
TEACHERS.XG0 + TEACHERS.YG0 TeacherFacultyIdx ChairNo TeacherNo 866

Обратить внимание: имя TeacherFacultyIdx содержит слово Faculty, но фактическое поле индекса — ChairNo, то есть кафедра. Название индекса не следует превращать в недоказанную связь преподавателя непосредственно с факультетом.

3. Для чего эти индексы нужны в приложении§

  • PlanNoIdx ускоряет получение занятий по записи учебной нагрузки/плана.
  • TeacherNoIdx ускоряет отбор занятий конкретного преподавателя.
  • GroupStreamIdx помогает получать группы, включенные в поток.
  • PlanGroupIdx позволяет переходить от записи плана к связанным группам и подгруппам. Этот порядок начинается с PlanNo, в отличие от первичного ключа GRPPLAN, который начинается с GroupNo.
  • StreamNameIdx обеспечивает поиск/сортировку потоков по названию.
  • TeacherFacultyIdx по содержимому позволяет выбирать преподавателей кафедры.

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

4. Как установлены поля и как выполнена проверка§

В .XG* содержательные поля вторичного ключа предшествуют полям первичного ключа исходной таблицы. Завершающее служебное поле имеет имя Blk Num и тип Short. Например, полный набор Classes.XG0: PlanNo, ClassNo, Blk Num. Для GRPPLAN.XG0 полный набор: PlanNo, GroupNo, Subgroup, GroupNo, Subgroup, PlanNo, Blk Num. Повторяющиеся имена здесь ожидаемы; запрет повторяющихся полей из обычного .DB-парсера к нему неприменим.

Имена полей всех индексов, кроме GRPPLAN.XG0, начинаются со смещения 0xDD; у GRPPLAN.XG0 — с 0xF5. После имен расположены номера полей, идентификатор сортировки cyrr и имя индекса. Полные смещения и размеры каждого поля находятся в JSON-доказательствах.

Прочитаны все записи всех шести .XG* по активным цепочкам блоков. Проверены размеры записей, предыдущие/следующие блоки, отсутствие циклов и совпадение числа записей с заголовком. Для каждого индекса мультимножество «вторичный ключ + первичный ключ» сравнено с соответствующей .DB: все шесть совпали. Это более строгая проверка, чем одно сравнение количества строк.

У STREAMS.XG0 текстовые ключи хранятся прописными буквами, тогда как STREAMS.DB сохраняет исходный регистр. Все 140 ключей совпали после приведения регистра. Это согласуется с нормализацией для поиска без учета регистра. При построении веб-каталога отображаемое название нужно брать из .DB, а не из индекса. Для остальных пяти индексов числовые значения совпали без нормализации.

Дополнительная диагностика Blk Num§

Числовое поле Blk Num дополнительно сравнили с фактическим номером блока, в котором сейчас находится строка с тем же первичным ключом в .DB. Получились расхождения:

Индекс Расхождений Blk Num с текущим блоком таблицы
Classes.XG0 1 765
Classes.XG1 370
GROUPS.XG0 14
GRPPLAN.XG0 1 126
STREAMS.XG0 140
TEACHERS.XG0 406

Это диагностическое наблюдение, не заключение о повреждении индексов. Значения ключей полностью согласованы, а точная актуальность/обновление служебной подсказки Blk Num в используемой версии BDE здесь не проверены. Нельзя на основании этого поля автоматически исправлять или перестраивать исходники. Новый просмотрщик может надежно извлекать записи из самих .DB по их активной цепочке блоков; для проверки старого индексного поиска нужно отдельно открыть рабочую копию в оригинальном окружении. Индексное дерево .YG* целиком не проверялось.

5. Практический вывод для веб-слоя§

Извлеченные вторичные ключи дают готовую основу индексов SQLite/PostgreSQL: Classes(PlanNo), Classes(TeacherNo), Groups(StreamNo), GrpPlan(PlanNo, GroupNo, Subgroup), Streams(StreamName) с продуманной русской сортировкой, Teachers(ChairNo). Это рекомендации для новой базы, а не изменения старых файлов.

Служебные .XG/.YG/.PX полезны для описания архитектуры и независимой проверки выгрузки, но веб-интерфейс не должен выдавать их за отдельные предметные таблицы или копировать служебный Blk Num в бизнес-модель расписания.