2009-09-18 10 views
0

我有一个表像这样:我需要最新纪录联接(PostgreSQL的)

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的事实并不完全有助于提高性能。有没有任何理由让他们作为TEXT而不是,例如,varchar(或甚至整数/ bigint的call_id)? – ChssPly76 2009-09-18 19:28:26

+0

撇开的类型 - 在哪个主题上,我同意你,顺便说一句 - 任何想法如何优化它原来的? – 2009-09-18 19:57:12

+0

TEXT和varchar之间没有性能差异。一个整数显然会更快,但如果模型支持。 – 2009-09-19 14:19:59

回答

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表,并在插入'started'时插入到call_activity中插入触发器,并在插入'completed'时删除。

0

更改数据类型为你的ID和尝试这样的事情(添加“开始”向他人过滤器的列表,如果你想包括在最新的未完成活动“开始”,以及):

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 2009-09-18 20:40:37

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; 

我没有猜测这是否比其他一些提议的表现更好或更差。我更喜欢它,因为(对我而言)它更具可读性。但可读性的确必须在这种情况下对性能产生影响。