Porównywanie dwóch sąsiednich rekordów, które mogą nie mieć kolejnego PK

0

Cześć

Mam taką tabelkę, do której lądują nagrane akcje wszystkich użytkowników w danych aplikacjach. Akcją może być np. klik myszką, wpisanie tekstu lub po prostu focus na daną aplikację.
Wygląda to mniej więcej tak:

screenshot-20220329083930.png
Jak widać akcja 5 jest nieprawidłowa, ponieważ nie może być tak, że przed focusem są jakieś akcja dla użytkownika A i aplikacji Powerpoint.
Pierw focus a potem akcje.
Chciałbym móc takie rzeczy wychwycić.
W tym celu zrobiłem złączenie dwóch tablic - a właściwie tej samej tylko z sąsiadującym wierszem:

select * from akcje a1 INNER JOIN akcje a2 ON a2.id_akcji = a1.id_akcji + 1 where a1.id_uzytkownik = a2.id_uzytkownik AND a1.Aplikacja != a2.Aplikacja and a2.akcja != 'Focus'

I generalnie to chodzi ale tylko jak w tabeli są wiersze obok siebie tego samego użytkownika. Np. między id_akcji 4 i 5 działa to: użytkownik A, są różne apllikację i ta (id_akcji + 1) nie jest focusem.

Ale jakby ta akcja 5 była np. 7 a 5 i 6 byłyby dla użytkownika B to już by to nie zadziałało. Byłaby dziura w numeracji.

Czuję, że musiałbym pogrupować tą tabelę akcji użytkownikami, potem nadać im jakąś nową numerację by była ciągła dla użytkownika i potem zrobić takiego joina jak powyżej.
Ale zabrakło wiedz :)

0

Analizuj kursorowo a nie zbiorowo bo problem masz kursorowy

Sortuj po ID i lec po kolei.

Jesli masz analizowac kilku użytkowników naraz to albo po stronie backendu (Java, C# itp) albo potrzebujesz kogos mocnego z PL/SQL - do implementacji i utrzymania.

2

Nie wiem jakiej bazy używasz, ale większość baz ma już zaimplementowane funkcje okienkowe, które w tym wypadku będą pomocne: https://pl.seequality.net/funkcje_lag_lead_w_tsql/

0

Może czegoś nie zrozumiałem, ale wybieramy te akcje użytkownika w ramach aplikacji, dla których nie istnieje wcześniejszy akcja Focus ?

select 
  a.* 
from akcje a
where not exists (select 1 from akcje a2 where a2.id_uzytkownika = a.id_uzytkownika and a2.aplikacja=a.aplikacja and a2.akcja='Focus' and a2.id_akcji<=a.id_akcji)

<= żeby wychwycić operacje typu: Focus, Focus, Focus...

0

@yarel: To całkiem inne podejście niż sobie w głowie poukładałem . Fajne!
Czy jednak ten select pokryje mi sytuację, że w jednym przypadku dla użytkownika A i aplikacji Word zadziałało i pierw był focus, a potem np. clicki. Następnie użytkownik przełączył się na excela tam działał i znowu wrócił do worda i tu nie pykło bo załóżmy pierw był click, a potem focus?

Bo z tego co widzę oznacza '1' wszystkie rekordy w przypadku gdy zgadzają się użytkownicy i aplikację i mają idki większe od tego 'badanego focusa.'
Dobrze myślę? Musiałbym jakoś dodać, że nie oznaczać '1' wszystkich rekordów tylko te do czasu wystąpienia kolejnego focusa dla tego samego usera

Mam 2 funkcje focusa - FOCUS i FOCUS1. Jak to poprawnie wkomponować w Twoje zapytanie?

0

@lipkerson: całość opiera się na wybraniu tych akcji, dla których nie jest spełniony pewien warunek (WHERE NOT EXISTS ( ZBIÓR )). Jeśli ZBIÓR jest pusty, to warunek (NOT EXISTS) jest spełniony. Interesuje nas fakt, czy zbiór jest pusty albo nie-pusty. To jakie elementy siedzą w tym zbiorze, jest bez znaczenia, tzn. zamiast '1' możesz użyć 'fasola' czy null (zbiór składający się z NULLi nie jest pusty).

ZBIÓR = wybieramy rekordy skorelowane z akcją - w tym przypadku czyli chcemy wybrać te dla których nie było focusa przed daną akcją.

Nie mam pojęcia jak ma się FOCUS do FOCUS1, więc zakładam, że obecność któregokolwiek z focusów jest wystarczająca:

select 
  a.* 
from akcje a
where not exists (select 1 from akcje a2 where a2.id_uzytkownika = a.id_uzytkownika and a2.aplikacja=a.aplikacja and a2.akcja in ('Focus','Focus2') and a2.id_akcji<=a.id_akcji)
0

@yarel: Tak, obecność, któregoś z nich jest wystarczająca. Dziękuję , muszę to sobie poukładać bo nie wykrywa mi części sytuacji.

0

Ogólnie przychodzi mi na myśl to:

A tak serio to popatrz na tą tabelę, którą masz i zastanów się na spokojnie skąd baza ma wiedzieć, że "FOCUS" był przed "CLICK" skoro ID akcji dla CLIK jest większy (znaczy był później wprowadzony do bazy). Dla Ciebie jest to logiczne ale dla bazy ten rekord był później. Możesz to zrobić na kilka sposobów ale musisz tym akcjom nadać odpowiednie priorytety/wagi/kolejność nazwij to jak chcesz ale w takiej postaci jak masz to teraz nie zrobisz tego dobrze.

Na chłopski rozum użytkownik pracuje tak: klika w aplikację -> odpala Ci się zdarzenie OnClick które odkłada wpis w bazie -> następnie uruchamia się OnFocus -> który robi drugi wpis do tabeli. W międzyczasie inny użytkownik robi swoje operacje i robi swoje wpisy ale ponieważ jest to jedna tabela to identyfikatory nie są po kolei. Jeżeli uważasz, że focus ma być zawsze przed innymi operacjami (nawet przed klik) to musisz sobie dopisać kolejną kolumnę na zasadzie

select a.id_uzytkownik, a.akcja, a.aplikacja, case when a.akcja = 'Focus' then 0 else 1 end , ... from akcje order by 1,4 

oczywiście to jest taki przykład pisany na sucho. Jak pykniesz tabelę na http://sqlfiddle.com/ to spróbuję Ci konkret napisać ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1