我需要连接中的最新记录(PostgreSQL)

时间:2009-09-18 19:09:37

标签: sql optimization postgresql query-optimization

我有一张这样的表:

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个可能的事件中的一个)中结束。我需要有两列的视图:第一列必须是调用'started'事件的活动时间,第二列必须是该调用的最新事件。此视图必须只有没有已完成事件的调用。

我有一组嵌套连接,但它们很慢。我需要一个合理的最佳视图。任何人都可以帮助我吗?

4 个答案:

答案 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;

我不确定这是否比其他一些提议表现更好或更差。我更喜欢它,因为(对我来说)它更具可读性。但是可读性确实必须在这样的事情上落后于性能。

答案 1 :(得分: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在已完成的事件中(与连接条件中的检查相同)。另一个是activity_type的索引,可能是部分只有'已启动'事件来加速初始扫描。最后,如果每次调用有大量事件,call_id,activity_time索引将加速子查询。如果您在主键中重新排序activity_type和activity_time,也可以获得该结果。

为了快速实现这一点,我将创建一个只包含call_id列的active_calls表,并在call_activity上添加一个插入触发器,如果​​插入'started'则插入active_calls,如果插入'completed'则删除。

答案 2 :(得分:0)

更改您的ID的数据类型,并尝试这样的事情(如果您希望在最新的未完成的活动中包含'started',请将“已启动”添加到其他过滤器列表中):

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

答案 3 :(得分: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