Полиморфизм
Как мы отмечали ранее в этом разделе, в объектной модели ODMG поддерживается семантика включения, т.е. в экстент суперкласса входят объекты экстентов всех его подклассов. Предположим, например, что мы определили класс PROGRAMMERS как наследника класса EMP через EXTENDS . Пусть для программистов меняется семантика метода HIGHER _ PAID (высокооплачиваемым программистом считается тот, кто получает более 30000 руб. в месяц). Тогда в классе PROGRAMMERS этот метод должен быть переопределен (с сохранением сигнатуры).
Понятно, что в соответствии с семантикой включения запрос из прим. 2.13 должен распространяться на всех служащих, включая программистов. Но если текущий объект E является объектом класса PROGRAMMERS , то к нему должен применяться переопределенный вариант метода HIGHER _ PAID . Тем самым, в OQL применятся отложенное связывание объектов и методов.
Еще одна возможность, связанная с полиморфизмом и поддерживающаяся в OQL , состоит в том, что пользователь может явно указать конкретный тип подкласса объектов коллекции, а система будет проверять правильность этого указания во время выполнения запроса. Предположим, что в классе PROGRAMMERS определен дополнительный атрибут favorite _ language , значение которого характеризуют “любимый” язык программирования каждого программиста. Кроме того, пусть известно, что в отделе 632 работают только программисты. Тогда возможен следующий запрос.
Пример 2.14. Получить названия любимых языков программирования сотрудников отдела 632.
SELECT DISTINCT ((PROGRAMMERS)E).favorite_language
FROM EMPLOYEES AS E
WHERE E.EMP_NO = 632
Если во время выполнения запроса будет обнаружено, что среди служащих отдела 632 имеются не только программисты, система зафиксирует ошибочную ситуацию.
Композиция операций
OQL является полностью функциональным языком. Если не нарушается система типов, то допускается произвольная композируемость операций. Этот подход отличается от подхода языка SQL , в котором правила композиции операций не являются ортогональными46.
Наличие полной ортогональности правил композиции в OQL позволяет не ограничивать мощность выражений и облегчить общее понимание языка, сохраняя возможность использования SQL -подобного синтаксиса при формулировке наиболее простых запросов.
Среди операций, которые поддерживаются в OQL и еще не упоминались в этом разделе, содержатся “множественные” операции union , intersect и except для всех видов коллекций; операции с кванторами for all и exists ; операции сортировки и группирования (sort и group by ); агрегатные операции count , sum , min , max и avg . Мы не будем подробно обсуждать эти операции47, но приведем пример достаточно сложного запроса, в котором некоторые из операций используются.
Пример 2.15. Найти название отдела, в котором работают служащие, средняя зарплата которых меньше средней зарплаты служащих любого другого отдела (для простоты предположим, что размер средней зарплаты служащих во всех отделах различается).
Будем строить запрос по шагам с использованием конструкции языка OQL define для вычисления промежуточных результатов.
Шаг 1. Построить множество объектов-служащих, для которых известен номер отдела.
define EMPLOYEES_D () AS
SELECT E FROM EMPLOYEES E
WHERE E.WORKS IS NOT nil
Шаг 2. Сгруппировать служащих с известными номерами отделов по номерам отделов и вычислить размер средней зарплаты для каждого отдела.
DEFINE DEPT_AVG_SAL () AS
SELECT DEPT_NO,
AVG_SAL : avg (SELECT X.E.EMP_SAL FROM PARTITION X )
FROM EMPLOYEES_D () E
GROUP BY DEPT_NO : E.DEPT_NO
Типомрезультатаэтогозапросаявляетсяbag < struct { integer DEPT_NO; decimal AVG_SAL } > . Операция group by расщепляет множество служащих на разделы (partitions ) в соответствии с критерием группирования (номеру отдела, в котором работает служащий). В разделе SELECT для каждого раздела вычисляется размер средней зарплаты сотрудников.
Шаг 3. Отсортировать полученное мультимножество по значениям атрибута AVG _ SAL .
DEFINE SORTED_DEPT_AVG_SAL () AS
SELECT S FROM DEPT_AVG_SAL () AS S ORDER BY S.AVG_SAL
Типомрезультатаявляетсяlist < struct { integer DEPT_NO; decimal AVG_SAL } > .
Шаг 4. Выбрать значение атрибута DEPT _ NO из элемента списка SORTED _ DEPT _ AVG _ SAL с наименьшим значением AVG _ SAL (этот элемент стоит в списке первым).
FIRST (SORTED_DEPT_AVG_SAL ()).DEPT_NO
Если собрать запрос целиком, то мы получим следующую конструкцию:
FIRST (SELECT DEPT_NO,
AVG_SAL : avg (SELECT X.E.EMP_SAL FROM PARTITION X )
FROM (SELECT E FROM EMPLOYEES E
WHERE E.WORKS IS NOT nil) AS E
GROUP BY DEPT_NO : E.DEPT_NO
ORDER BY AVG_SAL).DEPT_NO