我總是想知道在某個外鍵的歷史表中找到最後一個條目的正確方法是什麼?獲取歷史表的每個外鍵最後一項的正確方法是什麼?
例子:
我們假設我們有一個表是這樣的:
tHistory(ID | FK_User |國|時間戳)
什麼是Oracle中的有道閱讀爲每個FK_User ID加入條目?我總是使用一個子選擇,但它看起來並不適合我......任何其他方式來做到這一點?
我總是想知道在某個外鍵的歷史表中找到最後一個條目的正確方法是什麼?獲取歷史表的每個外鍵最後一項的正確方法是什麼?
例子:
我們假設我們有一個表是這樣的:
tHistory(ID | FK_User |國|時間戳)
什麼是Oracle中的有道閱讀爲每個FK_User ID加入條目?我總是使用一個子選擇,但它看起來並不適合我......任何其他方式來做到這一點?
另一種可能是使用相關子查詢,以確定最後的日期......
SELECT
*
FROM
tHistory
WHERE
timestamp = (
SELECT
MAX(timestamp)
FROM
tHistory latest
WHERE
FK_User = tHistory.FK_User
)
SELECT *
FROM (
SELECT h.*,
ROW_NUMBER() OVER
(PARTITION BY h.FK_User ORDER BY timestamp DESC) AS rn
FROM tHistory h
)
WHERE rn = 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。
通過合理的索引(在這種情況下爲{fk_user,timestamp}),這實際上既快又可讀。 – Ronnis 2011-03-22 16:02:36
如果該用戶有兩個或多個相同的時間戳,則這可能會導致多行用戶。取決於如何使用結果,這可能會導致問題。 – honeyp0t 2012-05-31 11:59:56
@ honeyp0t - 絕對正確。這只是考慮自己的一種方式。有了時間戳數據,它不太可能,因爲它幾乎是一個連續的值,但你是對的;取決於數據的「上次」可能與多個記錄相關的行爲。那麼問題就變成了「如果是這樣的話,是否應該返回一個或所有記錄,用於該時間戳?」 – MatBailie 2012-05-31 12:58:55