Я работаю программистом баз данных, в основном занимаюсь тем, что пишу процедуры на PL/SQL. Специальность у меня — Информационные технологии, так что к созданию программ на основе процедурного подхода подхожу довольно грамотно — разбиваю сложные алгоритма на части, те еще бью, и так до процедур размером в 20-50 строк. Когда так получается, я считаю, что программа написана правильно. Но иногда встречаются задачи, которые очень трудно разбить, или при разбиении получается слижком громоздкие конструкции. И программа получается довольно сложной. Смотрю я на нее, и впадаю в уныние — потому что упростить не могу
В общем вопрос — как бояться со сложно-алгоритмо-фобией? =)
01.11.08 12:04: Перенесено модератором из 'О жизни' — Хитрик Денис
Здравствуйте, minorlogic, Вы писали:
__>>В общем вопрос — как бояться со сложно-алгоритмо-фобией? =)
M>Пример в студию , может не все так плохо то ?
Действительно, было бы интересно увидеть примеры. Ведь многие классы задач, требующих многоэтажные конструкции if или switch при процедурном подходе,
очень изящно решаются с помощью ООП (Джаву можно подключить через PL/SQL). Кстати, это уже будет не алгоритм в классическом понимании этого слова, а какая-нибудь "бизнес-логика", под которую ООП заточено очень хорошо.
Здравствуйте, __kain, Вы писали:
__>Привет всем!
__>И программа получается довольно сложной. Смотрю я на нее, и впадаю в уныние — потому что упростить не могу
Ну и что? В жизни встречаются сложные вещи.
__>В общем вопрос — как бояться со сложно-алгоритмо-фобией? =)
А не нужно с ней бороться, это полезная фобия. Она удержит вас от того, чтобы всю жизнь искать философский камень и не найти.
__>Я работаю программистом баз данных, в основном занимаюсь тем, что пишу процедуры на PL/SQL. Специальность у меня — Информационные технологии, так что к созданию программ на основе процедурного подхода подхожу довольно грамотно — разбиваю сложные алгоритма на части, те еще бью, и так до процедур размером в 20-50 строк. Когда так получается, я считаю, что программа написана правильно. Но иногда встречаются задачи, которые очень трудно разбить, или при разбиении получается слижком громоздкие конструкции. И программа получается довольно сложной. Смотрю я на нее, и впадаю в уныние — потому что упростить не могу
Конкретно для PL/SQL большое количество строк не так уж страшно, т.к. процедура на много строк может сожержать один-единственный SQL-запрос, код которого может быть даже не на один экран. (причем длинный код запроса получается при аккуратном его форматировании, т.е. когда каждая значащая часть кода вынесена на отдельную строку). И в большинстве случаев правильнее этот запрос так и оставить и поручить его оптимизацию базе, чем пытаться вручную декомпозировать (и, соотвественно, получить вместо одного SQL-запроса несколько).
А бороться с фобией сложных процедур надо традиционными методами — подробным комментированием: что делается, зачем и почему именно так
Сейчас попытаюсь в двух словах описать проблему, которую я решаю, а позже приведу код.
На данный момент, я перерабатываю систему учета посещаемости студентов ВУЗа (пишу заново). Данные для оценки посещений получаются автоматически, за счет проходных турникетов. Можно сказать, что такой подход не точен (мало ли где в универе студент шляется), но практика показала, что если в системе посещаемости студент не ходит, то он и в действительности не ходит.
Старая система работает абсолютно негибко — всякие "неприятности", вроде переноса с одного дня занятий на другой (например, в связи с праздниками), системой никак не обрабатываются, и в результате и без того грубые данные еще сильней портятся.
Наверное, не стоит углубляться в дебри заморочек, именуемых бизнесс-правилами, обрисую общую ситуацию. Есть шаблон расписания, с привязкой к дате начала его действия. У расписания есть временные интервалы — периоды, в течение которых человек должен быть "внутри". Расписания бывают разных приоритетов (1-основное, 2-исключений). Накладывая 2 на 1, получаем "реальное" положение дел.
Для того чтобы оценить, когда кто должен ходить, нужно сгенерировать последовательность интервалов с привязками к конкретной дате и времени, а потом в этой последовательности указывать, кто и как ходил. Так вот самое неприятное — генерировать эту последовательность... Потому как нужно правильно отметить студента, учесть правила, ... В этом моменте наслаиваются правила трех подсистем — "Расписание", "Кадры студентов", "Рабочая нагрузка на преподавателей". И поэтому генерировать не просто В общем все это — приамбула к коду ))))))))
__>Для того чтобы оценить, когда кто должен ходить, нужно сгенерировать последовательность интервалов с привязками к конкретной дате и времени, а потом в этой последовательности указывать, кто и как ходил. Так вот самое неприятное — генерировать эту последовательность... Потому как нужно правильно отметить студента, учесть правила, ... В этом моменте наслаиваются правила трех подсистем — "Расписание", "Кадры студентов", "Рабочая нагрузка на преподавателей".
Если вот это все написать в виде комментариев внутри PL/SQL-процедуры, то код уже покажется не таким страшным, как раньше. Тут общий принцип такой: чем сложнее код и чем менее он очевиден — тем больше в нем надо комментариев
А своим первым постом я хотел сказать, что именно для PL/SQL длиная функция и сложная функция — это совсем не одно и то же. Потому что код на PL/SQL — это симбиоз двух подходов к программированию: алгоритмического (чистый PL/SQL) и декларативного (SQL)
Пусть, например, есть какая-нибудь такая функция:
FUNCTION some_proc() RETURN INTEGER
IS
x INTEGER;
BEGIN
SELECT <что-то> INTO x
FROM <много-много таблиц>
WHERE( <много-много условий> )
AND EXISTS(
SELECT <огромадный вложенный запрос на 100 строк кода...>
...
<и т.д.>
)
RETURN x;
END some_proc;
Сложный ли здесь алгоритм? Алгоритм здесь тривиальный: сделали запрос, вернули его результат.
Сложный ли запрос внутри этого алгоритма? Да, запрос сложный.
Надо ли в этом примере декомпозировать алгоритм? Нет, декомпозировать здесь абсолютно нечего. Главное — к запросу и его частям написать хорошие комментарии.
Кстати, если рассматривать запрос на SQL, то для него некий аналог декомпозиции — это завернуть часть запроса в отдельное представление (view). Но это не для всякого запроса можно сделать.
Здравствуйте, __kain, Вы писали:
__>Сейчас попытаюсь в двух словах описать проблему, которую я решаю, а позже приведу код.
__>На данный момент, я перерабатываю систему учета посещаемости студентов ВУЗа (пишу заново). Данные для оценки посещений получаются автоматически, за счет проходных турникетов. Можно сказать, что такой подход не точен (мало ли где в универе студент шляется), но практика показала, что если в системе посещаемости студент не ходит, то он и в действительности не ходит.
Хм... Вообще-то последнее утверждение тривиально — если, конечно, нельзя пройти помимо турникета.
Если не секрет — какой университет ? Мне просто как преподавателю интересно... Честно говоря, мне бы и в голову не пришло бы, что такое где-нибудь может быть.
Здравствуйте, klopodav, Вы писали:
K>А своим первым постом я хотел сказать, что именно для PL/SQL длиная функция и сложная функция — это совсем не одно и то же. Потому что код на PL/SQL — это симбиоз двух подходов к программированию: алгоритмического (чистый PL/SQL) и декларативного (SQL)
K>Сложный ли здесь алгоритм? Алгоритм здесь тривиальный: сделали запрос, вернули его результат. K>Сложный ли запрос внутри этого алгоритма? Да, запрос сложный. K>Надо ли в этом примере декомпозировать алгоритм? Нет, декомпозировать здесь абсолютно нечего. Главное — к запросу и его частям написать хорошие комментарии.
Абсолютно с Вами согласен!
K>Кстати, если рассматривать запрос на SQL, то для него некий аналог декомпозиции — это завернуть часть запроса в отдельное представление (view).
Да, в принципе я так и делаю. Однообразные соединения отправляю во вью, а потом их использую. В общем, подождите пару дней, не буду голословным и приведу пример!
2 Pavel Dvorkin
ВУЗ Ростовский, называется РГУПС (Ростовский Государственный Университет Путей Сообщения).
Задумка получения "халявных" данных о посещаемости хорошая, осталось ее до ума довести А вообще эта система — любимая погремушка деканатов и проректоров
Здравствуйте, __kain, Вы писали:
__>Привет всем!
__>Я работаю программистом баз данных, в основном занимаюсь тем, что пишу процедуры на PL/SQL. Специальность у меня — Информационные технологии, так что к созданию программ на основе процедурного подхода подхожу довольно грамотно — разбиваю сложные алгоритма на части, те еще бью, и так до процедур размером в 20-50 строк. Когда так получается, я считаю, что программа написана правильно. Но иногда встречаются задачи, которые очень трудно разбить, или при разбиении получается слижком громоздкие конструкции. И программа получается довольно сложной. Смотрю я на нее, и впадаю в уныние — потому что упростить не могу
__>В общем вопрос — как бояться со сложно-алгоритмо-фобией? =)
Сложно посоветовать что-то конкретное, поэтому ограничусь
общими советами.
1. Сначала надо разобраться с логической структурой базы данных,
упростить в случае необходимости. Сложность алгоритмов зависит
от того, насколько хорошо спроектирована база данных. В твоем случае
структура базы довольно сложная, тем более что используются темпоральные
данные, так что этому надо уделить особое внимание.
2. Процедурный код упрощается путем разбиения на части, как ты и делаешь.
Код со сложной логикой (признак такого кода — много сложных вложенных операторов
if, в которых трудно разобраться) можно упростить, например, если сначала
описать решение при помощи таблицы решений, а потом уже на ее основе
писать код.
3. Сложные SQL-запросы можно упростить несколькими способами:
— Использовать представления для выделения отдельных частей
запроса, как уже было сказано выше.
— Использовать WITH для улучшения читабельности запроса
(описано, например, в блоге Кевина Мида).
— Писать свои функции для упрощения сложных выражений в запросах
— Использовать табличные функции.
Уже обсуждалось и тут тоже... CTE (Common Table Expressions) называется... Очень удобно использовать для рекурсий, построения деревьев, например, в MS SQL... в оракле для этого давно CONNECT BY есть, при этом в Оракле CTE и CONNECT BY можно смешивать ...
Вот в MS SQL есть табличные функции с параметрами, а в Оракле ничего подобного нет, но в Оракле есть Пакеты, а подобного нет в MS SQL... они словно сговорились... =((
2 Flying Dutchman > В твоем случае структура базы довольно сложная, тем более что используются темпоральные данные, так что этому надо уделить особое внимание.
Структуру я весь диплом проектировал, и потом еще месяц =) Она получилась универсальной и вроде более-менее простой, но, действительно, наличие темпоральных данных только усложняет дело. Причем все эти данные могут потом еще и перегенерироваться (полностью и частично)... Я тут подумал, нужно наверное в структуру еще что-то добавить, чтобы в коде нужно было проводить меньше вычислений.
> Код со сложной логикой (признак такого кода — много сложных вложенных операторов if, в которых трудно разобраться)
В моем случае, это большая вложенность и большая осмысленность каждого курсора выборки данных...
> 3. Сложные SQL-запросы можно упростить несколькими способами:
Спасибо
В общем я понял, нет неразрешаемых проблем, просто мне нехватает теоретической базы Буду изучать предложенную матчасть
Наконец принес код. Прошу не вчитываться в него, это только пробный вариант, да и непонятен он будет. Просто взгляните на картинку
DECLARE
PROCEDURE BuildIntervalSeq(p_att_User IN
p_date_from IN TIMESTAMP := NULL,
p_date_to IN TIMESTAMP := NULL) IS
v_priority_idz isa_att.TimeTableType.idz%TYPE;
v_time_table_idz isa_att.TimeTable.idz%TYPE;
v_index BINARY_INTEGER;
v_date_offset TIMESTAMP;
v_interval_offset TIMESTAMP;
v_time_step INTERVAL DAY TO SECOND := p_att_common.GetSingleAdjustment('STUD_RASP_DUR');
TYPE IntervalArray IS TABLE OF isa_att.TimeInterval%ROWTYPE;
v_intervalArray IntervalArray;
-- Выбрать типы расписаний по приоритетам.CURSOR cTimeTablePriority IS
SELECT idz
FROM isa_att.TimeTableType
ORDER BY priority ASC;
-- Выбираем расписания, принадлежащие
-- иерархическим группам студентов.CURSOR cTimeTableCur IS
SELECT TimeTable_view.*
FROM isa_att.TimeTable_view,
isa_att.HierarchyGroup
WHERE HierarchyGroup.s_HierarchyType = p_att_common.GetSingleAdjustment('STUD_HIERARCHY')
AND TimeTable_view.s_Timetabletype = v_priority_idz
AND TimeTable_view.s_HierarchyGroup = HierarchyGroup.Idz
AND SYSTIMESTAMP BETWEEN TimeTable_view.Validfrom
AND NVL(TimeTable_view.ValidTo, SYSTIMESTAMP);
CURSOR cTimeIntervals IS
SELECT *
FROM isa_att.TimeInterval
WHERE s_TimeTable = v_time_table_idz
ORDER BY fl_nad_chertoy DESC, TimeOffset;
-- Выбираем расписание и валидных
-- юзеров для этого времениCURSOR cTimeTableValidView IS
SELECT *
FROM isa_att.TimetableValid_View
WHERE s_TimeTable = v_time_table_idz
AND s_attUser = (SELECT idz FROM isa_att.AttUser where s_teksost = 40351);
BEGIN
v_intervalArray := IntervalArray();
FOR curPriority IN cTimeTablePriority LOOP
v_priority_idz := curPriority.Idz;
FOR curTimeTable IN cTimeTableCur LOOP
v_time_table_idz := curTimeTable.Idz;
-- Выбираем в массив для многократного использованияOPEN cTimeIntervals;
FETCH cTimeIntervals BULK COLLECT INTO v_intervalArray;
CLOSE cTimeIntervals;
FOR curUserValidView IN cTimeTableValidView LOOP
-- Получили временную метку начала расписания: отступаем "назад" до понедельника,
-- т.к. занятия начинаются не обязательно в понедельник, а отсчет
-- временных интервалов начинается с понедельника.
v_date_offset := curTimeTable.ValidFrom - TO_NUMBER(TO_CHAR(curTimeTable.Validfrom, 'D')) + 1;
-- <Перебираем даты>
LOOP
EXIT WHEN v_date_offset > curTimeTable.ValidTo;
-- <Перебираем интервалы>
v_index := v_intervalArray.FIRST;
LOOP
EXIT WHEN v_index IS NULL;
v_interval_offset := v_intervalArray(v_index).TimeOffset + v_date_offset;
IF v_intervalArray(v_index).fl_nad_chertoy = 0 THEN
v_interval_offset := v_interval_offset + 7;
END IF;
IF v_interval_offset >= curTimeTable.ValidFrom THEN--dbms_output.put_line(TO_CHAR(v_interval_offset, 'DD.MM.YYYY HH24:MI'));
DoLogic (v_interval_offset,
v_intervalArray(v_index).Duration,
curUserValidView.s_AttUser,
curUserValidView.Validfrom,
curUserValidView.Validto,
curTimeTable.s_Hierarchygroup,
curTimeTable.Idz,
v_intervalArray(v_index).idz);
END IF;
v_index := v_intervalArray.NEXT(v_index);
END LOOP; -- </Перебираем интервалы>
v_date_offset := v_date_offset + v_time_step;
END LOOP; -- </Перебираем даты>END LOOP; -- </Перебираем пользователей>END LOOP;
END LOOP;
END;
BEGIN
BuildIntervalSeq;
END;
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Здравствуйте, __kain, Вы писали:
__>>Сейчас попытаюсь в двух словах описать проблему, которую я решаю, а позже приведу код.
__>>На данный момент, я перерабатываю систему учета посещаемости студентов ВУЗа (пишу заново). Данные для оценки посещений получаются автоматически, за счет проходных турникетов. Можно сказать, что такой подход не точен (мало ли где в универе студент шляется), но практика показала, что если в системе посещаемости студент не ходит, то он и в действительности не ходит.
PD>Хм... Вообще-то последнее утверждение тривиально — если, конечно, нельзя пройти помимо турникета.
PD>Если не секрет — какой университет ? Мне просто как преподавателю интересно... Честно говоря, мне бы и в голову не пришло бы, что такое где-нибудь может быть.
Такое даже в некоторых школах (в Москве) есть.
У учеников есть персональные магнитные карты, которые они прикладывают к турникету. В этот момент на экране монитора у охранника высвечивается фотография ученика и какие-то данные (наверное опаздывает ли учник в соответствии с расписанием). Если опаздывает, могут поругать =))
А также записывается в базу данных время входа и выхода из школы. В конце каждого месяца данные распечатываются и передаются классному руководителю.
Здравствуйте, maggot, Вы писали:
M>Такое даже в некоторых школах (в Москве) есть. M>У учеников есть персональные магнитные карты, которые они прикладывают к турникету. В этот момент на экране монитора у охранника высвечивается фотография ученика и какие-то данные (наверное опаздывает ли учник в соответствии с расписанием). Если опаздывает, могут поругать =)) M>А также записывается в базу данных время входа и выхода из школы. В конце каждого месяца данные распечатываются и передаются классному руководителю.
У мну подсистема связана с кадрами, рабочей нагрузкой и учебным расписанием. Отчет выдается в виде соответствия расписания и посещения его человеком, т.е. автоматизация полная. Если сделать тупо, то да, за месяц можно. А если сделать нормально — с учетом плавающих, периодических и других графиков посещений, то тут прийдется извилины напрягать!