我有一张这样的表:
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'事件的活动时间,第二列必须是该调用的最新事件。此视图必须只有没有已完成事件的调用。
我有一组嵌套连接,但它们很慢。我需要一个合理的最佳视图。任何人都可以帮助我吗?
答案 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