2010-11-11 4 views
5

Używam Oracle PL/SQL.W języku PL/SQL, jak zaktualizować wiersz na podstawie następnego wiersza?

Mam tabelę T z sygnaturą czasową i chcę ustawić wartość wiersza dla kolumny A na taką samą, jak w poprzednim wierszu, jeśli są sortowane według kolumn B i datownika, o ile znaczniki czasu nie są różni się o więcej niż 45 sekund.

W Pseudokod, to coś jak:

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 
    ) 

Próbowałem to:

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 
    ) 

Ale mam:

Error (38,16): PL/SQL: ORA-00934: funkcja grupowa jest tutaj niedozwolona

wskazuje na pierwszą instancję RANKU.

Co zrobiłem źle i jak mam to naprawić?

+1

Chciałem zasugerować użycie 'lag' lub' lead', ale to też może nie działać ... lub możesz spróbować 'update T set a = wybierz Q1.A z ((wybierz A, rownum r1 z T) Q1 opuścił zewnętrzne sprzężenie (wybierz A, rownum r2 z T) Q2 na Q1.r1 = Q2.r2-1) ' – FrustratedWithFormsDesigner

+0

@FrustratedWithFormsDesigner - Masz rację, że opóźnienie i lead sprawiają, że mam ten sam problem. Mam coś na podstawie twoich innych sugestii do kompilacji, więc, dzięki! Jeśli chcesz go skopiować na odpowiedź, którą zaakceptuję, nic by się nie stało. –

+0

Zrobione! (Na początku napisałem jako komentarz, ponieważ nigdy nie próbowałem tego dla aktualizacji i nie byłem pewien, czy to zadziała);) – FrustratedWithFormsDesigner

Odpowiedz

3

Spróbuj użyć instrukcji łączenia. Nie jestem pewien, czy robi to, co chcesz, ale powinno działać. Niestety klauzula insert jest konieczna), ale nigdy nie powinna być wywoływana.

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

Dzięki! Mam coś na ten temat do skompilowania, i ma to dla mnie sens, że powinno zadziałać –

+1

Nadal uważam, że może zajść potrzeba spojrzenia na Twoje wymagania Co się stanie, gdy będziesz mieć kilka transakcji w ciągu 45 sekund od siebie? Na przykład trzy rzędy w odstępie 40 sekund? –

1

można spróbować czegoś takiego:

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

Mam wrażenie, że spowodowałoby to problemy z wydajnością, prawda? Mam do czynienia z dziesiątkami tysięcy rzędów. –

+1

@ MOE37x3 Nie zakładałbym niczego ... W zależności od tego, z kim rozmawiasz, dziesiątki tysięcy wierszy naprawdę nie jest tak wiele. – Fosco

0

można spróbować (może potrzebować trochę szczypanie, aby zrobić to dobrze, ale idea jest dwa identyczne zamówione podzapytania połączone przez przesunięcie 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

Wygląda na to, że rownum nie bierze pod uwagę porządku. Robi to po prostu w kolejności, w której dostęp do wierszy był możliwy. http://www.dbforums.com/oracle/988716-rownum-order.html –

+0

@ MOE37x3: Wiem, mój pierwszy nie miał żadnego zamówienia. Dodałem kolejność w B i polu znacznika czasu. Czy to Ci pasuje? – FrustratedWithFormsDesigner

+0

Zgodnie z tym, co przeczytałem, rownum jest stosowane przed złożeniem zamówienia, więc numery będą tam, a wiersze będą w podanej kolejności, ale liczby mogą być w innej kolejności. –

1

I kolejna opcja ... nie dość tego, co chcę, ponieważ ignoruje wymóg, aby posortować na B, ale może to daje do myślenia .... Bez definicji tabeli i rzeczy to było trochę trudno uzyskać uchwyt dokładnie to, co było wymagane.

Edytuj: po ponownym przeczytaniu pytania wygląda na to, że Twoja składnia jest nieprawidłowa. Funkcje grupowe (ołów/opóźnienie/pozycja itp.) Mogą pojawiać się tylko na liście wyboru lub w klauzuli kolejność. Są one oceniane po złączeniach, klauzulach grupowania i posiadania. Powinno zadziałać coś, co pokazano poniżej.

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

Dziękuję za wyjaśnienie błędu w moim kodzie. –

0

Co można zrobić.

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 

Wymaga to, aby kolumna, którą chcesz zaktualizować, była pusta, chyba że chcesz zaktualizować wszystkie.

Powiązane problemy