Мне нужна самая последняя запись в соединении (PostgreSQL) - PullRequest
0 голосов
/ 18 сентября 2009

У меня есть таблица примерно так:

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 или около того возможных). Мне нужно посмотреть на это с двумя столбцами: первый столбец должен быть временем активности события «старт» вызова, второй столбец должен быть самым последним событием для этого вызова. В этом представлении должны быть только вызовы, для которых нет завершенных событий.

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

Ответы [ 4 ]

1 голос
/ 15 июня 2011

Это решение, которое я тестировал без какой-либо индексации, и для невероятно небольшого набора данных, поэтому оно потребует некоторых настроек для вашей среды. Вам понадобится индекс по крайней мере для 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;

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

0 голосов
/ 19 сентября 2009

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

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 голосов
/ 22 сентября 2009

Решение без объединений с использованием оператора 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
0 голосов
/ 19 сентября 2009

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

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».

...