2011-03-22 31 views
3

我總是想知道在某個外鍵的歷史表中找到最後一個條目的正確方法是什麼?獲取歷史表的每個外鍵最後一項的正確方法是什麼?

例子:

我們假設我們有一個表是這樣的:

tHistory(ID | FK_User |國|時間戳)

什麼是Oracle中的有道閱讀爲每個FK_User ID加入條目?我總是使用一個子選擇,但它看起來並不適合我......任何其他方式來做到這一點?

回答

4

另一種可能是使用相關子查詢,以確定最後的日期......

SELECT 
    * 
FROM 
    tHistory 
WHERE 
    timestamp = (
    SELECT 
     MAX(timestamp) 
    FROM 
     tHistory latest 
    WHERE 
     FK_User = tHistory.FK_User 
) 
+0

通過合理的索引(在這種情況下爲{fk_user,timestamp}),這實際上既快又可讀。 – Ronnis 2011-03-22 16:02:36

+0

如果該用戶有兩個或多個相同的時間戳,則這可能會導致多行用戶。取決於如何使用結果,這可能會導致問題。 – honeyp0t 2012-05-31 11:59:56

+0

@ honeyp0t - 絕對正確。這只是考慮自己的一種方式。有了時間戳數據,它不太可能,因爲它幾乎是一個連續的值,但你是對的;取決於數據的「上次」可能與多個記錄相關的行爲。那麼問題就變成了「如果是這樣的話,是否應該返回一個或所有記錄,用於該時間戳?」 – MatBailie 2012-05-31 12:58:55

2
SELECT * 
FROM (
     SELECT h.*, 
       ROW_NUMBER() OVER 
       (PARTITION BY h.FK_User ORDER BY timestamp DESC) AS rn 
     FROM tHistory h 
     ) 
WHERE rn = 1 
+0

你看了我的最後一句話? :) – nino 2011-03-22 15:28:06

+1

@nino:是的,我做到了。你確定你沒有混淆子選擇('標量=(SELECT ...)')和內嵌視圖('SELECT FROM(SELECT ...)')嗎? – Quassnoi 2011-03-22 15:31:05

+0

與Quassnoi同意的術語,行話的一個坑跌倒(爲什麼我嘗試使用更多的英語單詞,而不是更少的技術的話) – MatBailie 2011-03-22 15:33:58

1

好問題。

版本8I之前,你可能需要使用子查詢,而您需要訪問thistory表兩次。 當引入分析功能,您可以通過使用內嵌視圖裏面的ROW_NUMBER解析函數跳過第二表格訪問,就像Quassnoi已經表現出你。

然而,由於9版本有一個更美好,更高性能的Oracle中的方式。 僅由FK_USER分組並使用第一個或最後一個集合函數。

FIRST:http://download.oracle.com/docs/cd/B10501_01/server.920/a96540/functions45a.htm#SQLRF00641 LAST:http://download.oracle.com/docs/cd/B10501_01/server.920/a96540/functions57a.htm#83735

這是一個帶表thistory一個例子:

SQL> create table thistory (id,fk_user,state,timestamp) 
    2 as 
    3 select 1, 'Me', 'D', sysdate from dual union all 
    4 select 2, 'Me', 'C', sysdate-1 from dual union all 
    5 select 3, 'Me', 'B', sysdate-2 from dual union all 
    6 select 4, 'Me', 'A', sysdate-3 from dual union all 
    7 select 5, 'You', 'B', sysdate-11 from dual union all 
    8 select 6, 'You', 'A', sysdate-12 from dual 
    9/

Table created. 

SQL> exec dbms_stats.gather_table_stats(user,'thistory') 

PL/SQL procedure successfully completed. 

SQL> alter session set statistics_level = all 
    2/

Session altered. 

SQL> set serveroutput off 

首先預8I變體與所述子查詢:

SQL> select * 
    2 from thistory 
    3 where timestamp = 
    4   (select max(timestamp) 
    5    from thistory latest 
    6   where fk_user = thistory.fk_user 
    7  ) 
    8/

     ID FK_USER S TIMESTAMP 
---------- ------- - ------------------- 
     1 Me  D 19-05-2011 11:20:48 
     5 You  B 08-05-2011 11:20:48 

2 rows selected. 

SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last')) 
    2/

PLAN_TABLE_OUTPUT 
-------------------------------------------------------------------------------------------------------------------------- 
SQL_ID 306v8p42zdz34, child number 0 
------------------------------------- 
select * from thistory where timestamp =  (select max(timestamp)   from thistory latest 
     where fk_user = thistory.fk_user  ) 

Plan hash value: 2894184026 

---------------------------------------------------------------------------------------------------------------------- 
| Id | Operation   | Name  | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem | 
---------------------------------------------------------------------------------------------------------------------- 
|* 1 | HASH JOIN   |   |  1 |  2 |  2 |00:00:00.01 |  7 | 1155K| 1155K| 478K (0)| 
| 2 | VIEW    | VW_SQ_1 |  1 |  2 |  2 |00:00:00.01 |  3 |  |  |   | 
| 3 | HASH GROUP BY  |   |  1 |  2 |  2 |00:00:00.01 |  3 |  |  |   | 
| 4 |  TABLE ACCESS FULL| THISTORY |  1 |  6 |  6 |00:00:00.01 |  3 |  |  |   | 
| 5 | TABLE ACCESS FULL | THISTORY |  1 |  6 |  6 |00:00:00.01 |  4 |  |  |   | 
---------------------------------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    1 - access("TIMESTAMP"="VW_COL_1" AND "FK_USER"="THISTORY"."FK_USER") 


22 rows selected. 

現在解析函數的變體:

SQL> select * 
    2 from (select h.* 
    3    , row_number() over (partition by h.fk_user order by timestamp desc) as rn 
    4    from thistory h 
    5  ) 
    6 where rn = 1 
    7/

     ID FK_USER S TIMESTAMP     RN 
---------- ------- - ------------------- ---------- 
     1 Me  D 19-05-2011 11:20:48   1 
     5 You  B 08-05-2011 11:20:48   1 

2 rows selected. 

SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last')) 
    2/

PLAN_TABLE_OUTPUT 
-------------------------------------------------------------------------------------------------------------------------- 
SQL_ID b7zscht24wa2s, child number 0 
------------------------------------- 
select * from (select h.*    , row_number() over (partition by h.fk_user order by timestamp desc) 
as rn   from thistory h  ) where rn = 1 

Plan hash value: 2357375523 

-------------------------------------------------------------------------------------------------------------------------- 
| Id | Operation    | Name  | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem | 
-------------------------------------------------------------------------------------------------------------------------- 
|* 1 | VIEW     |   |  1 |  6 |  2 |00:00:00.01 |  3 |  |  |   | 
|* 2 | WINDOW SORT PUSHED RANK|   |  1 |  6 |  6 |00:00:00.01 |  3 | 9216 | 9216 | 8192 (0)| 
| 3 | TABLE ACCESS FULL  | THISTORY |  1 |  6 |  6 |00:00:00.01 |  3 |  |  |   | 
-------------------------------------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    1 - filter("RN"=1) 
    2 - filter(ROW_NUMBER() OVER (PARTITION BY "H"."FK_USER" ORDER BY INTERNAL_FUNCTION("TIMESTAMP") DESC)<=1) 


21 rows selected. 

最後最有效的變體使用聚合函數:

SQL> select max(id) keep (dense_rank last order by timestamp) id 
    2  , fk_user 
    3  , max(state) keep (dense_rank last order by timestamp) state 
    4  , max(timestamp) 
    5 from thistory 
    6 group by fk_user 
    7/

     ID FK_USER S MAX(TIMESTAMP) 
---------- ------- - ------------------- 
     1 Me  D 19-05-2011 11:20:48 
     5 You  B 08-05-2011 11:20:48 

2 rows selected. 

SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last')) 
    2/

PLAN_TABLE_OUTPUT 
-------------------------------------------------------------------------------------------------------------------------- 
SQL_ID 9gcbnuf776q27, child number 0 
------------------------------------- 
select max(id) keep (dense_rank last order by timestamp) id  , fk_user  , max(state) keep 
(dense_rank last order by timestamp) state  , max(timestamp) from thistory group by fk_user 

Plan hash value: 76026975 

-------------------------------------------------------------------------------------------------------------------- 
| Id | Operation   | Name  | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem | 
-------------------------------------------------------------------------------------------------------------------- 
| 1 | SORT GROUP BY  |   |  1 |  2 |  2 |00:00:00.01 |  3 | 9216 | 9216 | 8192 (0)| 
| 2 | TABLE ACCESS FULL| THISTORY |  1 |  6 |  6 |00:00:00.01 |  3 |  |  |   | 
-------------------------------------------------------------------------------------------------------------------- 


14 rows selected. 

如果你仔細觀察的計劃,你會看到,最後一個是最有效的。 而且它不需要內聯視圖。

希望這會有所幫助。

Regards,
Rob。

相關問題