2010-11-11 30 views
5

我正在使用Oracle PL/SQL。在PL/SQL中,如何根據下一行更新一行?

我有一個時間戳表T,我想設置一個列A的值與上一行的相同,如果它們按列B和時間戳排序,假設時間戳不是相差超過45秒。

僞代碼,它是這樣的:

UPDATE T t_curr 
    SET A = 
    (SELECT A 
     FROM T t_prev 
     INNER JOIN t_curr 
     ON (t_prev is the row right before t_curr, when you sort by B and Timestamp) 
      AND t_curr.Timestamp - t_prev.Timestamp < 45 
    ) 

我嘗試這樣做:

UPDATE T t_curr 
    SET A = 
    (SELECT A 
     FROM T t_prev 
     INNER JOIN t_curr 
     ON RANK (t_curr) 
      OVER (B, Timestamp) 
      = 1 + RANK (t_prev) 
      OVER (B, Timestmap) 
      AND t_curr.Timestamp - t_prev.Timestamp < 45 
    ) 

但我得到:

錯誤(38,16):PL/SQL: ORA-00934:此處不允許使用組功能

指向RANK的第一個實例的

我做錯了什麼,以及我如何得到這個權利?

+1

我正要建議使用'lag'或'lead',但這可能無法工作......或者您可以嘗試更新T set a =從Q中選擇Q1.A((從T中選擇A,rownum r1) Q1離開外部連接(從T中選擇A,rownum r2)Q1.r1 = Q2.r2-1)' – FrustratedWithFormsDesigner 2010-11-11 15:33:22

+0

@FrustratedWithFormsDesigner - 你說得對,滯後和導致同樣的問題。儘管如此,我根據您的其他建議編譯了某些內容,非常感謝!如果你想把它複製到我接受的答案中,那就沒問題。 – 2010-11-11 15:46:13

+0

完成! (我起初發表評論,因爲我從來沒有嘗試過這個更新,並不知道它會工作);) – FrustratedWithFormsDesigner 2010-11-11 15:53:39

回答

3

嘗試使用合併語句。不確定它完全符合你的要求,但它應該可以工作。不幸的是插入條款是必要的),但不應該被稱爲。

merge into t a 
using (
    select 
    A, 
    B, 
    timestamp, 
    lag(A) over (order by id, timestamp) as prior_A, 
    lag(timestamp) over (order by B, timestamp) as prior_timestamp 
    from t) b 
on (a.B = b.B) 
when matched then 
    update set a.a = case when b.timestamp-b.prior_timestamp <= 45 
    then b.prior_A else b.A end 
when not matched then insert (B) values (null) 
+0

謝謝!我得到了一些基於此的東西來進行編譯,而且它對我來說應該是有用的。 – 2010-11-11 21:08:53

+1

我仍然認爲你可能需要看看你的要求當你在45秒之內完成幾次交易時會發生什麼?例如,三行相隔40秒? – 2010-11-11 22:28:09

1

你可以嘗試這樣的事:

update x 
set x = y.A 
from T x 
join T y 
where x.B = (select MAX(B) from T where B < y.B) 
and x.Timestamp = (select MAX(Timestamp) from T where Timestamp < y.Timestamp) 
and y.Timestamp - x.Timestamp < 45 
+0

我覺得這樣會導致性能問題,不是嗎?我正在處理數以萬計的行。 – 2010-11-11 15:37:43

+1

@ MOE37x3我不會假設任何東西......根據你所說的話,成千上萬的行實際上並沒有那麼多。 – Fosco 2010-11-11 15:49:17

0

你可以嘗試(可能需要一些調整,以得到它的權利,但這個想法是兩個相同的有序的子查詢由偏移rownumbers加入)

update T set a = (select A1 
       from (
         select S1.A A1, rownum r1 
         from (select * from T order by B, timestamp) S1 
         left outer join 
         select S2.A A2, rownum r2 
         from (select * from T order by B, timestamp) S2 
         on r1 = r2-1 
        ) 
       ) 
+0

它看起來像rownum不考慮秩序。它只是按行訪問的順序進行。 http://www.dbforums.com/oracle/988716-rownum-order.html – 2010-11-11 16:00:54

+0

@ MOE37x3:我知道,我的第一個沒有訂購。我在B和時間戳字段上添加了排序。那對你有用嗎? – FrustratedWithFormsDesigner 2010-11-11 16:06:27

+0

根據我讀過的內容,rownum在訂購前會被應用,所以數字會在那裏,行將按照指定的順序排列,但數字可能會按照其他順序排列。 – 2010-11-11 16:09:29

1

而另一種選擇......並不完全做不想要的東西,因爲它忽略了需求進行排序B,而它可能給你一些思考....不表定義和事情是有點難以掌握所需要的東西。

編輯:再次閱讀問題時,它看起來像你的語法是錯誤的。組功能(領先/滯後/排名等)只能出現在選擇列表或排序條款中。他們在連接之後進行評估,在那裏,分組並且有條款。所以像下面顯示的東西應該工作。

update T a 
set A = (select 
    new_A 
    from (
    select 
    B, 
    A, 
    timestamp, 
    first_value(A) 
     over (order by timestamp range between 45 preceding and current row) as new_A 
    from mike_temp_1 
) b where b.id = a.id) 
+0

感謝您解釋我的代碼中的錯誤。 – 2010-11-11 21:07:54

0

你可以做的是。

update t 
set colToUpdate = nextValue 
from (
select A 
     ,B 
     ,C 
     ,(LEAD(B, 1, null) over (order by A)) as nextValue 
    FROM db.schema.table 
) as t 
    where colToUpdate is null 

這要求您要更新的列爲空,除非您要更新所有列。