2012-03-26 79 views
2

給定一個表動作(開始:DATE,長度:NUMBER,類型:NUMBER),所有記錄都是唯一的,我需要在時間前選擇(例如)類型爲Y的最後一個動作的長度X:選擇最接近但不大於行

select action.length 
where action.type = Y 
    and action.start is the biggest, but not greater than X 

提出的解決方案(改善):

with actionView as (select * from action where type = Y and time <= X) 
select length 
    from actionView 
where time = (select max(time) from actionView) 

但是,這仍然envolves 2點進行選擇。

我想問的是有可能對此查詢執行一些分析或分層或任何其他oracle魔法來改進它?

(也許,這樣的算法中是我需要的,但我不知道該如何表達它的SQL:

savedAction.time = MinimalTime 
foreach action in actions 
    if action.type = y and savedAction.time < action.time <= X 
    savedAction = action 
return savedAction; 

+0

你應該尋找LAG功能。您可以對數據進行排序並與以前的行進行比較。此外,您可能會感到驚訝的是,優化器對結構比您更快樂:) – Randy 2012-03-26 12:40:58

回答

3

Oracle與其他RDBMS一樣沒有LIMIT(PostgreSQL,MySQL)或TOP(SQL Server)子句。但是你可以使用ROWNUM爲:

SELECT * 
FROM (
    SELECT length 
    FROM action 
    WHERE type = Y 
    AND start < X 
    ORDER BY start DESC 
    ) 
WHERE rownum = 1; 

這樣,該表將被一次性查詢。
The details in the manual


在回答民主黨發表評論我從上面的鏈接引用:

如果嵌入的ORDER BY子句中的子查詢並把ROWNUM 條件在頂級查詢,那麼你可以強制在排序後應用ROWNUM 條件。

+0

Oracle是否真的保證***外部查詢中數據的順序?如果在內部查詢中有'rownum AS row_id',然後通過'row_id = 1'過濾了外部查詢,那麼我相信結果會有一個有保證的行爲。 – MatBailie 2012-03-26 13:00:36

+0

@Erwin Brandstetter在您的解決方案和Dems(以上)之間有真正的區別嗎?我不確定是否存在語義差異,但速度如何? – 2012-03-26 13:06:59

+1

from oracle docs:'如果將ORDER BY子句嵌入到子查詢中,並將ROWNUM條件置於頂級查詢中,則可以強制ROWNUM條件在行排序後應用 – 2012-03-26 13:08:51

0

我不知道任何魔法,但是:

with (select length, time from action where type = Y and time <= X) as typed_action 
    select length from typed_action 
    where time = (select max(time) from typed_action) 

會給你更少的「where」子句和一個(很多?)更小的臨時typed_action表。

+0

SQL不是直接執行,而是編譯成計劃。這對最終結果可能只有很小的影響或沒有影響;它與在主要查詢中指定'type = Y和time <= X'過濾器相同。 – MatBailie 2012-03-26 12:58:50

+0

@boisvert上面提到了單一掃描結果。你怎麼看待他們? – 2012-03-26 13:06:04

+0

@RustemMustafin,比我的解決方案更整潔;儘管您應該對時間列進行索引以加速排序,因爲性能是一個問題。另外,如果你的過濾數據很小,那麼排序沒有發生,但如果有很多它,max更快。 – boisvert 2012-03-26 14:09:37

2

您可以使用ROW_NUMBER()在一次掃描,以評估這...

WITH 
    sequenced_data 
AS 
(
    SELECT 
    ROW_NUMBER() OVER (PARTITION BY x ORDER BY start DESC) AS sequence_id, 
    * 
    FROM 
    action 
    WHERE 
    type = Y 
    AND start < Z 
) 
SELECT 
    * 
FROM 
    sequenced_data 
WHERE 
    sequence_id = 1 

你不需要PARTITION BY,但它是用在你得到「最大」每行(例如,每個人或數據庫中的項目)的行。

+0

我想你的解決方案非常接近Erwin(下面)的解決方案。有沒有真正的區別? – 2012-03-26 13:05:08

+1

@RustemMustafin - Erwin建議RowNum速度更快,可能是這種情況,你必須測試,但我自己也無法說出任何一種方式。此外,如答案中所述,這可以擴展爲爲多個組執行此操作。如果您想爲1000名員工中的每一位僱員提供最新的進入時間,則此模式會在一次查詢中產生所有1000個結果。 – MatBailie 2012-03-26 13:36:01

+0

+1,應該是'按類型分區',或者你可以刪除分區的子句(在這種情況下是無用的),並且其中應該是'start 2012-03-26 13:37:57