Для этого БД должен как минимум найти все начальные вызовы и выяснить, существуют ли для него какие-либо завершенные действия. Предполагая, что незавершенным является небольшой набор, выборка самого последнего действия может быть выполнена как подзапрос. Вот запрос, который делает это:
SELECT c_started.call_id, c_started.activity_id AS started_time,
(SELECT MAX(c_recent.activity_time)
FROM call_activity AS c_recent
WHERE c_recent.call_id = c_started.call_id) AS recent_activity
FROM call_activity AS c_started
LEFT JOIN call_activity AS c_completed
ON c_started.call_id = c_completed.call_id
AND c_completed.activity_type IN ('completed 1' 'completed 2', ...)
WHERE c_started.activity_type = 'started'
AND c_completed.call_id IS NULL;
Если вы можете добавить индексы, первым выбором будет частичный индекс для call_id, где активность_типа находится в завершенных событиях (такая же проверка, как в условии соединения). Другим может быть индекс по типу Activity_type, возможно частичный, с только «запущенными» событиями для ускорения начального сканирования. Наконец, call_id, индекс activity_time ускорит подзапрос, если у вас много событий на вызов. Вы также можете получить это, если вы переупорядочите activity_type и activity_time в первичном ключе.
Чтобы сделать это быстро, я бы создал таблицу active_calls только со столбцом call_id и добавил бы триггер вставки в call_activity для вставки в active_calls, если вставлено «start», и удалил, если вставлено «complete».