2009-09-18 56 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; 

我沒有猜測這是否比其他一些提議的表現更好或更差。我更喜歡它,因爲(對我而言)它更具可讀性。但可讀性的確必須在這種情況下對性能產生影響。