2009-09-18 3 views
0

У меня есть таблица, например, так:мне нужна самая последняя запись в Join (PostgresSQL)

call_activity (
    call_id TEXT, 
    activity_type TEXT, 
    activity_time TIMESTAMP, 
    PRIMARY KEY(call_id, activity_type, activity_time) 
) 

activity_type может быть один из примерно 9 различных строк:

'started' 
'completed' (about 5 variations on this) 
'other' (these are the states that I want to display) 

Вызов имеет серии событий, начиная с «начатого» и заканчивая завершенным событием (одним из 5 возможных). Мне нужно взглянуть на это с двумя столбцами: первый столбец должен быть временем активности события «запущено», второй столбец должен быть самым последним событием для этого вызова. В этом представлении должны быть только вызовы, для которых не завершено событие.

У меня есть набор вложенных соединений на нем, но они медленны, как ад. Мне нужно разумно оптимальное представление об этом. Может кто-нибудь мне помочь?

+1

Тот факт, что у вас есть как call_id, так и activity_type как TEXT, не совсем помогает в отношении производительности. Есть ли причина иметь их как ТЕКСТ, а не, скажем, varchar (или даже целое/bigint для call_id)? – ChssPly76

+0

Оставляя в стороне типы - по какой теме я согласен с вами, кстати, любая идея, как оптимизировать ее как-есть? –

+0

Не существует разницы в производительности между TEXT и varchar. Очевидно, что целое число будет быстрее, если модель поддерживает это. –

ответ

0

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

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 в завершенных событий (тот же чек, что и в условии объединения). Другим будет индекс активности_type, возможно, частичный с только «начальными» событиями, чтобы ускорить первоначальное сканирование. Наконец, call_id, index_time_time ускорит подзапрос, если у вас много событий за звонок. Вы также можете получить это, если вы переупорядочиваете activity_type и activity_time в первичном ключе.

Чтобы сделать это быстро, я бы создал таблицу active_calls с только столбцом call_id и добавил триггер insert в call_activity для вставки в active_calls, если «start» вставлен и удален, если вставлен «завершен».

0

Изменить типы данных для удостоверения личности, и попробовать что-то вроде этого (добавить «начало» в список других фильтра, если вы хотели бы включить «начали» в последней Незавершенной деятельности, а):

SELECT  ca_s.activity_time AS timestamp_started, 
      ca_o.activity_time AS timestamp_other 
FROM  call_activity ca_s 
LEFT JOIN call_activity ca_o 
     ON ca_s.call_id = ca_o.call_id 
     AND ca_o.activity_type IN ('other-1', 'other2-2', ...) 
LEFT JOIN call_activity ca_c 
     ON ca_s.call_id = ca_c.call_id 
     AND ca_s.activity_type IN ('completed-1', 'completed-2', ...) 
WHERE  ca_s.activity_type = 'started' 
     AND ca_c.call_id IS NULL --// no complete events 
+0

плюс, индекс на call_id поможет в любом случае – van

0

раствор без соединения, с помощью оператора CASE, и группировка

select call_id , 
     min(case when activity_type = 'started' then activity_time 
       else null 
      end) as timestamp_started, 
     max(activity_time) as timestamp_other 
from call_activity 
group by call_id 
having 
     sum(case when activity_type = 'completed-1' then 1 
       when activity_type = 'completed-2' then 1 
       else 0 
      end) = 0 
1

это решение я тестировал без индексации, и на невероятно небольшой набор данных, так что потребуется некоторые настройки для вашей среды. Вам понадобится индекс, по крайней мере, для call_id (duh!) И activity_type. Он также использует настраиваемую агрегированную функцию, LAST() (во многих моих собственных проектах я использую аналогичную функцию FIRST()).

CREATE OR REPLACE FUNCTION slast(anyelement,anyelement) RETURNS anyelement AS $$ 
    SELECT $2 
$$ LANGUAGE sql IMMUTABLE STRICT; 

CREATE AGGREGATE last (
    sfunc = slast, 
    basetype = anyelement, 
    stype = anyelement 
); 

CREATE VIEW current_calls AS 
SELECT min(activity_time) AS call_started,last(activity_type) AS current_activity 
FROM (
    SELECT call_id,activity_time,activity_type 
    FROM call_activity 
    WHERE call_id NOT IN (SELECT call_id FROM call_activity WHERE activity_type='completed') 
    ORDER BY activity_time 
) AS x 
GROUP BY call_id; 

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

Смежные вопросы