Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: specyficzne sortowanie po określonych pozycjach
Forum PHP.pl > Forum > Bazy danych > MySQL
bnowak
Witam,

Po złożeniu selecta który składa się z kilku joinów, podzapytań itd. itp. otrzymuję w końcu zestaw danych (w uproszczeniu):

Kod
id |  extra  | order
----------------------
1  | 'value' | (1)
42 |   null  |  1
14 | 'value' | (2)
44 |   null  |  2
15 |   null  |  3
21 | 'value' | (4)
61 |   null  |  4
35 |   null  |  5
18 | 'value' | (6)
6  |   null  |  6


I teraz potrzebuję posortować go w taki sposób że rekordy o orderze w nawiasie znajdują się na dokładnej pozycji którą zawierają (na pozycji z kolumny order) a reszta jest niejako uzupełnieniem pozycji które nie są ustawione (z zachowaniem ich kolejności). Każdy rekord z orderem z nawiasem posiada wartości w dodatkowych kolumnach a bez nawiasu nie posiada tych wartości więc po tym mogę je rozrózniać. Mam nadzieję że jasne o co chodzi, w prawidłowej kolejności dane powinny wyglądać:

Kod
id |  extra  | order      
----------------------      
1  | 'value' | (1)      
14 | 'value' | (2)      
42 |   null  |  1      
21 | 'value' | (4)      
44 |   null  |  2      
18 | 'value' | (6)      
15 |   null  |  3      
61 |   null  |  4      
35 |   null  |  5      
6  |   null  |  6


Próbowałem dodawać nowy licznik do którego dodawana ilość rekordów z orderami w nawiasie ich poprzedzająca dany rekord ale uzyskuję w ten sposób złą kolejność.... :

Kod
id |  extra  | order    |    id |  extra  | order |  licznik                                  |  przy przepisywaniu pozycji z nawiasów (reszta po uzyskanej kolejnosci)
----------------------  |    ---------------------------------------------------------------
1  | 'value' | (1)      |    1  | 'value' | (1)   |   1 + (0 nawiasów poprzedzających) = 1    |  1
14 | 'value' | (2)      |    42 | 'value' |  1    |   1 + (1 nawias poprzedzający) = 2        |  2
42 |   null  |  1       |    14 |   null  | (2)   |   2 + (1 nawias poprzedzający) = 3        |  2
21 | 'value' | (4)      |    44 |   null  |  2    |   2 + (2 nawias poprzedzający) = 4        |  4
44 |   null  |  2       |    15 |   null  |  3    |   3 + (2 nawias poprzedzający) = 5        |  5
18 | 'value' | (6)      |    21 | 'value' | (4)   |   4 + (2 nawias poprzedzający) = 6        |  4
15 |   null  |  3       |    61 |   null  |  4    |   4 + (3 nawias poprzedzający) = 7        |  7
61 |   null  |  4       |    35 |   null  |  5    |   5 + (3 nawias poprzedzający) = 8        |  8
35 |   null  |  5       |    18 | 'value' | (6)   |   6 + (3 nawias poprzedzający) = 9        |  6
6  |   null  |  6       |    6  |   null  |  6    |   6 + (4 nawias poprzedzający) = 10       |  10
prawidłowa kolejność                              uzyskana kolejność


W obu sposobach jest zła kolejność. Może ktoś ma pomysł jak coś takiego rozwiązać ?

pozdrawiam
alegorn
raczej nie uda ci sie w prosty sposob tego osiagnac w mysql.
to co próbujesz osiągnąć - nie jest tym jak działa sort w mysql, (który układa w określonej kolejności)
ty potrzebujesz zarezerwować index (kolejność).

do php.

(no bo co, gdyby w nawiasach była 100 a wszystkich rekordów 20? mysql ma ci wstawić nulle questionmark.gif? )

j.
bnowak
Cytat(alegorn @ 10.10.2012, 10:37:30 ) *
(no bo co, gdyby w nawiasach była 100 a wszystkich rekordów 20? mysql ma ci wstawić nulle questionmark.gif? )


to nie problem, wtedy rekord z 100 w nawiasie powinien pokazać się na 20 pozycji
chodzi mi o zachowanie odpowiedniej kolejnosci
bostaf
Na pierwszy rzut oka nie wyobrażam sobie jak można to wykonać w czystym MySQL. Ale nie takie dziwolągi niektórzy rozwiązywali.
Naprawdę musisz to zrobić w czystym MySQLu? Czy czas wykonania obliczeń ma znaczenie? Czasami warto podzielić robotę między silnik bazy i silnik języka programowania w optymalny sposób wykorzystując zasoby całego systemu.
bnowak
Jeżeli rozwiążę to w MySql wszystkie problemy z przeliczaniem, przesuwaniem, paginacją itd. mam załatwione więc fajnie by było kolejność mieć już gotową w select'cie. Czas wykonania nie ma większego znaczenie jeżeli nie przekracza powiedzmy sekundy (chociaż jeżeli to zadziała to już sobie bardziej zoptymalizuje wewnętrzne select'y).

Myślałem coś w stylu licznika + zrobić zmienną do której będę wrzucał z ordery w nawiasach po przecinku (np. '5,213,12,53,') i później znowu przelatując po każdym z rekordów sprawdzać czy jest z nawiasem
wtedy przepisywać jego żądaną kolejność (inkrementować licznik),
a gdy nie jest z nawiasem wykonywać pętlę która sprawdza czy obecny licznik zawiera się w zmiennej z orderami "nawiasowymi" (za pomocą funkcji FIND_IN_SET - działa dobrze, sprawdzałem): jeżeli tak zwiększam licznik i znowu sprawdzam, w ten sposób po wyjściu z pętli powinienem dostać kolejny dostępny order (względem tych z nawiasami). Tylko w tym przypadku miałem problem z samą pętlą wykonywaną w select'cie - z tego co czytałem to używa się jej w funkcjach, i tutaj [zonk] user którym łączę się z bazą produkcyjną nie posiada praw do tworzenia funkcji (raczej nie dostanę tego przywileju ;/). Czy da się jednak w jakiś sposób wykonać tą pętlę w czystym selecta'cie ?
bostaf
Cytat(bnowak @ 10.10.2012, 13:27:21 ) *
Czy da się jednak w jakiś sposób wykonać tą pętlę w czystym selecta'cie ?

Ja nie potrafię. W PHP bym zrobił wg takiego algorytmu:
  • przygotowuję segregator z ilością przegródek równą ilości wyszukanych rekordów
  • numeruję przegródki w segregatorze od 1 do ilości wyszukanych rekordów
  • przeglądam rekordy po kolei odkładając na jedną stertę te z nawiasami a na drugą te bez
  • biorę pierwsza stertę i wkładam rekordy do segregatora, tak żeby numer na rekordzie odpowiadał numerowi przegródki w segregatorze
  • biorę druga stertę i segreguję po numerze rosnąco
  • biorę tą posortowaną stertę i po kolei od góry wkładam w puste przegródki segregatora
redeemer
Cytat(bnowak @ 10.10.2012, 13:27:21 ) *
Tylko w tym przypadku miałem problem z samą pętlą wykonywaną w select'cie - z tego co czytałem to używa się jej w funkcjach, i tutaj [zonk] user którym łączę się z bazą produkcyjną nie posiada praw do tworzenia funkcji (raczej nie dostanę tego przywileju ;/). Czy da się jednak w jakiś sposób wykonać tą pętlę w czystym selecta'cie ?
Bez stworzenia funkcji/procedury raczej tego zwykłym zapytaniem nie zrobisz.

Cytat(bnowak @ 10.10.2012, 13:27:21 ) *
Jeżeli rozwiążę to w MySql wszystkie problemy z przeliczaniem, przesuwaniem, paginacją itd. mam załatwione więc fajnie by było kolejność mieć już gotową w select'cie.
Nic nie stoi na przeszkodzie, że dane zwrócone przez zapytanie przetworzysz jeszcze w PHP.
bnowak
udało się smile.gif dla potomności:

ordery bez nawiasów przepisujemy, a dla tych z nawiasami tworzymy licznik który zwiększamy dla każdego rekordu z nawiasem a zanim go zwiększymy odejmujemy od wartości w nawiasie wartość licznika i ją zwracamy, wygląda to tak:
Kod
id |  extra   | order    |    id |  extra  | order |  nowyOrder; licznik = 0
----------------------  |    ---------------------------------------------------------------
1  | 'value' | (1)      |    1  | 'value' | (1)   |   1 - 0 = 1; licznik++ )(1);
14 | 'value' | (2)      |    42 |   null  |  1    |   1
42 |   null  |  1       |    14 | 'value' | (2)   |   2 - licznik = 1; licznik++ (2);
21 | 'value' | (4)      |    44 |   null  |  2    |   2
44 |   null  |  2       |    15 |   null  |  3    |   3
18 | 'value' | (6)      |    21 | 'value' | (4)   |   4 - licznik = 2; licznik++ (3);
15 |   null  |  3       |    61 |   null  |  4    |   4
61 |   null  |  4       |    35 |   null  |  5    |   5
35 |   null  |  5       |    18 | 'value' | (6)   |   6 - licznik = 3; licznik++ (4);
6  |   null  |  6       |    6  |   null  |  6    |   6
prawidłowa kolejność                              uzyskana kolejność

następnie sortujemy po nowymOrderze, w drugiej kolejności po starym orderze (najpierw z dodatkowymi kolumnami a następnie bez) i uzyskujemy dobrą kolejność smile.gif
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.
Invision Power Board © 2001-2025 Invision Power Services, Inc.