2015-09-19 20 views
5

Próbuję użyć tej rekurencyjnej funkcji SQL, ale nie mogę go dostać, aby zrobić to, co chcę, nawet blisko. Zakodowałem logikę w rozwiniętej pętli, pytając, czy można ją przekonwertować na pojedyncze rekurencyjne zapytanie SQL, a nie na styl aktualizacji tabeli, z którego korzystałem.Podkursoria Oracle rekursywne Factoring przekonwertować

http://sqlfiddle.com/#!4/b7217/1

Istnieje sześć graczy w rankingu. Mają id, identyfikator grupy, wynik i rangę.

Stan początkowy

+----+--------+-------+--------+ 
| id | grp_id | score | rank | 
+----+--------+-------+--------+ 
| 1 |  1 | 100 | (null) | 
| 2 |  1 | 90 | (null) | 
| 3 |  1 | 70 | (null) | 
| 4 |  2 | 95 | (null) | 
| 5 |  2 | 70 | (null) | 
| 6 |  2 | 60 | (null) | 
+----+--------+-------+--------+ 

Chcę podjąć osoba z najwyższym wynikiem początkowym i nadać im rangę 1. Wtedy stosuje się 10 punktów bonusowych na wynik każdego, kto ma ten sam identyfikator grupy. Weź następny najwyższy, przypisz rangę 2, rozdaj punkty bonusowe i tak dalej, dopóki nie pozostanie żaden gracz.

Identyfikator użytkownika zrywa więzy.

Punkty bonusowe zmieniają pozycję w rankingu. id = 4 początkowo wydaje się być na drugim miejscu z 95, za liderem z 100, ale z premią 10 punktów, id = 2 przesuwa się w górę i zajmuje miejsce.

stan końcowy

+-----+---------+--------+------+ 
| ID | GRP_ID | SCORE | RANK | 
+-----+---------+--------+------+ 
| 1 |  1 | 100 | 1 | 
| 2 |  1 | 100 | 2 | 
| 4 |  2 | 95 | 3 | 
| 3 |  1 | 90 | 4 | 
| 5 |  2 | 80 | 5 | 
| 6 |  2 | 80 | 6 | 
+-----+---------+--------+------+ 

Odpowiedz

2

Jest to trochę późno, ale nie jestem pewien, można to zrobić za pomocą rekurencyjne CTE. Zrobiłem jednak wymyślić rozwiązanie z zastosowaniem klauzuli Model:

WITH SAMPLE (ID,GRP_ID,SCORE,RANK) AS (
SELECT 1,1,100,NULL FROM DUAL UNION 
SELECT 2,1,90,NULL FROM DUAL UNION 
SELECT 3,1,70,NULL FROM DUAL UNION 
SELECT 4,2,95,NULL FROM DUAL UNION 
SELECT 5,2,70,NULL FROM DUAL UNION 
SELECT 6,2,60,NULL FROM DUAL) 
SELECT ID,GRP_ID,SCORE,RANK FROM SAMPLE 
MODEL 
DIMENSION BY (ID,GRP_ID) 
MEASURES (SCORE,0 RANK,0 LAST_RANKED_GRP,0 ITEM_COUNT,0 HAS_RANK) 
RULES 
ITERATE (1000) UNTIL (ITERATION_NUMBER = ITEM_COUNT[1,1]) --ITERATE ONCE FOR EACH ITEM TO BE RANKED 
(
RANK[ANY,ANY] = CASE WHEN SCORE[CV(),CV()] = MAX(SCORE) OVER (PARTITION BY HAS_RANK) THEN RANK() OVER (ORDER BY SCORE DESC,ID) ELSE RANK[CV(),CV()] END, --IF THE CURRENT ITEM SCORE IS EQUAL TO THE MAX SCORE OF UNRANKED, ASSIGN A RANK 
LAST_RANKED_GRP[ANY,ANY] = FIRST_VALUE(GRP_ID) OVER (ORDER BY RANK DESC), 
SCORE[ANY,ANY] = CASE WHEN RANK[CV(),CV()] = 0 AND CV(GRP_ID) = LAST_RANKED_GRP[CV(),CV()] THEN SCORE[CV(),CV()]+10 ELSE SCORE[CV(),CV()] END, 
ITEM_COUNT[ANY,ANY] = COUNT(*) OVER(), 
HAS_RANK[ANY,ANY] = CASE WHEN RANK[CV(),CV()] <> 0 THEN 1 ELSE 0 END --TO SEPARATE RANKED/UNRANKED ITEMS 
) 
ORDER BY RANK; 

To nie jest bardzo ładna, i podejrzewam, że nie ma lepszego sposobu, aby go o to, ale to nie daje spodziewanych rezultatów.

Ostrzeżenia:

Trzeba by zwiększyć liczbę iteracji, jeśli masz więcej niż ta liczba wierszy.

Dokonuje to pełnego ponownej oceny w oparciu o wynik po każdej iteracji. Gdybyśmy wzięli twoje przykładowe dane, ale zmienili początkowy wynik pozycji od 2 do 95, a nie 90: po zaliczeniu pozycji 1 i przyznaniu premii 10 punktów do pozycji 2, teraz ma ona wynik 105. Tak więc oceniamy go jako pierwszy i przenieś element 1 w dół do drugiego. Będziesz musiał wprowadzić kilka modyfikacji, jeśli nie jest to pożądane zachowanie.