2013-04-24 147 views
0

我在Oracle數據庫中有一個名爲my_table的表,例如。它是日誌表的類型。它有一個增量列,其名稱爲「id」和「registration_number」,它是註冊用戶的唯一。現在,我想註冊用戶最新的變化,所以我寫了下面的查詢來完成這個任務:選擇最優化查詢

第一個版本:

SELECT t.* 
FROM my_table t 
WHERE t.id = 
    (SELECT MAX(id) FROM my_table t_m WHERE t_m.registration_number = t.registration_number 
); 

第二個版本:

SELECT t.* 
FROM my_table t 
INNER JOIN 
    (SELECT MAX(id) m_id FROM my_table GROUP BY registration_number 
) t_m 
ON t.id = t_m.m_id; 

我的第一個問題是上面哪個查詢是推薦的,爲什麼?第二個是如果有時候大約有70.000個插入到這個表中,但是大多數插入行的數量在0到2000之間變化,那麼向這個表中添加索引是否合理?

回答

2

的分析查詢可能是得到每個註冊用戶的最新變化最快的方法:

SELECT registration_number, id 
FROM (
    SELECT 
    registration_number, 
    id, 
    ROW_NUMBER() OVER (PARTITION BY registration_number ORDER BY id DESC) AS IDRankByUser 
    FROM my_table 
) 
WHERE IDRankByUser = 1 

至於指標,我假設你已經通過registration_number有一個索引。 id上的附加索引將有助於查詢,但可能不會太多或可能不足以證明索引。我這樣說,因爲如果你一次插入70K行,額外的索引將減慢INSERT。你必須進行實驗(並檢查執行計劃)以確定該索引是否值得。

+0

感謝您的回答。實際上,起初我使用ROW_NUMBER()做了這個,但後來我認爲這不是最好的方式,所以我嘗試了其他方法來做到這一點。爲什麼認爲它可能會更快? – 2013-04-24 06:05:36

+0

我已經測試過,但速度較慢。感謝您的時間和索引建議 – 2013-04-24 06:25:28

+0

我可以想象一下'max'查詢會更快 - 如果oracle在索引上使用'min/max scan'。但是你在那裏得到了一個組,並且我不確定oracle可以對組合索引執行最小/最大掃描。值得檢查。 – haki 2013-04-24 08:57:41

2

爲了檢查更快的查詢,你應該檢查執行計劃和成本,它會給你一個公平的想法。但是我同意Ed Gibbs的解決方案,因爲分析使得查詢運行得更快。 如果你覺得這個表會變得非常大,那麼我會建議分區表和使用本地索引。他們一定會幫助你形成更快的查詢。

如果你想插入大量的行,那麼索引放慢插入,因爲每個插入索引也必須更新[我不會建議在ID上索引]。有兩種解決方案我想到這個:

  1. 您可以在插入之前刪除索引,然後在插入後重新創建它。
  2. 使用反向鍵索引。檢查這個鏈接:http://oracletoday.blogspot.in/2006/09/there-is-option-to-create-index.html。反向鍵索引可以影響你的查詢,所以會有折衷。
+0

感謝您對索引的建議,我會盡力實現這一點。但不幸的是,使用ROW_NUMBER()的查詢是其中最慢的一個 – 2013-04-24 09:05:33

0

如果你尋找更快的解決方案,有一個真正需要保持過去的活動列表中爲每個用戶,那麼最強大的解決方案是維護單獨的表具有獨特registration_number值,並在日誌表中創建的最後記錄的rowid

E.g.(僅用於演示,不檢查語法的有效性,序列和觸發器省略):

create table my_log(id number not null, registration_number number, action_id varchar2(100)) 
/

create table last_user_action(refgistration_number number not null, last_action rowid) 
/

alter table last_user_action 
    add constraint pk_last_user_action primary key (registration_number) using index 
/

create or replace procedure write_log(p_reg_num number, p_action_id varchar2) 
is 
    v_row_id rowid; 
begin 

    insert into my_log(registration_number, action_id) 
    values(p_reg_num, p_action_id) 
    returning rowid into v_row_id; 

    update last_user_action 
    set last_action = v_row_id 
    where registration_number = p_reg_num; 

end; 
/

有了這樣的架構可以爲具有良好性能的每一個用戶簡單的查詢,最後的動作:

select 
from 
    last_user_action lua, 
    my_log   l 
where 
    l.rowid (+) = lua.last_action 

ROWID是物理存儲標識直接尋址存儲塊,並且在移動到另一臺服務器,從備份等恢復後不能使用它。但是如果您需要這種功能,則可以簡單地將id列從my_log表添加到last_user_action,並且使用一個或另一個取決於要求。