Pilsener
3.03.2009, 11:44:11
Załóżmy mam tabelę:
ID||Lp||Nazwa
1||2||abc
2||4||xyz
3||1||qwerty
4||3||asd
Pole lp jest dodane po to, aby użytkownik mógł decydować o kolejności wyświetlania elementów (taki bajer, poza standardowym sortowaniem np. po nazwie)
Otóż w formularzu użytkownik sobie wybiera (lub wpisuje), że np. pozycja o ID 3 ma mieć pozycję 4-tą, więc trzeba jej zmienić LP na 4 a pozostałe rekordy odpowiednio zmodyfikować, by wyglądało tak:
ID||Lp||Nazwa
1||1||abc
2||3||xyz
3||4||qwerty
4||2||asd
Zrobiłem pętlę, która za jednym obrotem updatuje jeden rekord, jednak nie jest to zbyt wydajne rozwiązanie i zastanawiam się, czy nie można tego rozwiązać jednym-dwoma zapytaniami? Lub może mam złe podejście do problemu?
Stąd prośba do Was.
Ale po co Ci pętla, przetwarzasz wszystkie rekordy? Przecież musisz tylko zamienić dwa numery ze sobą (ten który poda user z tym, który aktualnie jest pod tym lp).
Pilsener
3.03.2009, 14:15:57
No jak, przecież zmieniają się wszystkie:
1 - z 2 na 1
2 - z 4 na 3
3- z 1 na 4
4 - z 3 na 2
Część lp spadnie o jedną pozycję (-1) a część wzrośnie (+1) - gdybym zrobił tak jak mówisz to albo miałbym dwa identyczne lp, albo zamieniłbym miejscami pozycje 1 z 4-tą, a po spadku ID 3 z pozycji 1 na 4-tą na pierwsze miejsce ma wskoczyć dotychczasowa druga pozycja, a nie czwarta - czwarta ma być teraz trzecia.
Nie wiem jak to lepiej wytłumaczyć...
Czyli to nie do końca jest sortowanie bo kolejność elementów zawsze będzie taka sama w tym systemie.
Nazwałbym to raczej określaniem, który element jest pierwszy. Więc może zamiast tego co robisz dodaj tylko jakiś znacznik mówiący o tym, który z rekordów ma być w tym momencie pierwszy?
Pilsener
3.03.2009, 14:36:39
Niestety to nic nie pomoże, musi być możliwość dowolnego poukładania rekordów poprzez przesuwanie pojedyńczej pozycji w górę lub w dół o n miejsc lub odgórne wskazanie miejsca w hierarchi. Po to właśnie zostało dodane do bazy to pole - pozostaje tylko elegancko ogarnąć zapytanie do tego.
Identyczny problem powstaje przy usuwaniu rekordu - "dziura" musi zostać ładnie wypełniona, ale tu widzę mniejszy problem, bo wszystkie lp dla rekordów poniżej skasowanego wystarczy updatować +1. Przy dodawaniu jest jeszcze łatwiej, bo nowy rekord automatycznie trafia na koniec, czyli max(lp)+1.
artur_dziocha
3.03.2009, 14:45:30
jesli dobrze rozumiem to są przeciez 2 operacje
1. znajdujesz element o lp niższym o 'n' i tam zwiekszasz o 'n'
2. zmniejsz o 'n' w zmienianych
I chyba to wszystko:)
z usuwaniem też możesz sobie latwo poradzić żeby nie bylo dziur. Po usunieciu wiersza pobierasz wszystko tworzysz tablice z 'id' i 'lp' jako ireacyjne $i
i później update bazy.
~ Literówki
Pilsener
3.03.2009, 14:59:03
Ok, czyli załóżmy, że mam 10 rekordów i chcę przesunąć rekord z 7-mej pozycji na 4-tą:
1. Rekordy 1-3 oraz 8-10 nie zmieniają się
2. Rekordy na pozycjach 4-6 dostają +1
3. Rekord 7 dostaje lp=4
Analogicznie przy przesuwaniu w dół np. z 3-ciej na 8-mą:
1. Rekordy 1-2 i 9-10 nie zmienią się
2. Rekordy na pozycjach 4-8 dostają -1
3. Rekord 3 dostaje lp=8
Chyba nie da się tego zrobić prościej, ale lepsze dwa zapytania niż po jednym dla każdego rekordu. Poeksperymentuję dziś wieczorem z tym
kefirek
3.03.2009, 21:57:44
Można tez spróbować tak powinno chyba dzialac
UPDATE id SET lp = CASE lp
WHEN id=2 THEN 7
WHEN lp > 7 THEN lp+1
ELSE lp-1 END
Gdzie 2 to id jakie zmieniasz
Warunek sprawdza czy ID jest równe temu ID co chcesz zmienić jak tak zmieniasz na podaną wartość potem warunek sprawdzasz czy LP jest większe od podanej wartości ( w tym przykładzie 7 )jak tak to dodaje 1 a na końcu sprawdzasz czy mniejsze jak tak odejmuje 1
mongea
4.03.2009, 09:15:17
hi
slp - stare lp
nlp - nowe lp
czyli np. z 4-tej pozycji (slp) na 7-ma (nlp)
jednym zapytaniem niezaleznie od kierunku/braku zmiany:
UPDATE tab
SET lp = IF (lp = slp, nlp, lp + SIGN(slp - nlp))
WHERE lp >= LEAST(nlp, slp) AND lp <= GREATEST(nlp, slp)
Pilsener
5.03.2009, 22:31:50
Wielkie dzięki, śmiga jak burza.
Mam jeszcze jedno pytanko, czy na podobnej zasadzie mogę sprowadzić numery rekordów:
id||lp
x||8
x||5
x||2
x||9
Do postaci:
id||lp
x||3
x||2
x||1
x||4
Także jednym zapytaniem? Kombinuję z tymi IF+CASE, ale moje doświadczenie w tym zakresie jest znikome, prosiłbym chociaż o wskazówkę.
Edit: dzięki ponownie, nie wiedziałem, że nawet można używać zmiennych w zapytaniach - toż to cały język

Bardzo mi to pomoże powiększyć funkcjonalność przy zachowaniu zdroworozsądkowej liczby zapytań do bazy.
mongea
7.03.2009, 21:14:37
ponumerowanie z pozbyciem sie luk i zachowaniem porzadku:
UPDATE tab SET lp = (SELECT @nlp:=@nlp+1 nlp FROM (SELECT @nlp:=0) flp) ORDER BY lp
@nlp - dowolna nazwa zmiennej
rozna nazwa aliasow (nlp i flp)
dzialaj
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę
kliknij tutaj.