Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP][MySQL] Zamiana wartości w tabeli
Forum PHP.pl > Forum > PHP
rolnix
Witam.

Specyfikacja sytuacji:

Elementy w moim systemie menu są sortowane według pola 'order' od zera w górę, w panelu administracyjnym jest opcja przesuwania ich w górę oraz i w dół. Oto funkcja, która służy mi do przykładowego przesunięcia danego elementu tablicy "w górę":

  1. <?php
  2. function up_menu_item() {
  3. global $sql;
  4. $menuitemid = GetPageArgument('menu_item_id', 0); // biorę id itemu do przesunięcia z $_GET
  5.  
  6. $sql->query('SELECT `order` FROM `###menuitems` WHERE `id`='.$menuitemid); // pobieram aktualną wartość pola 'order'
  7. if ($sql->num_rows() < 1) return; // jeśli nie ma takowego, przerywam
  8. $row = $sql->fetch_array();
  9. $baseitem_order = $row['order'];
  10.  
  11. $sql->query('SELECT `id` FROM `###menuitems` WHERE `order`='.($baseitem_order-1)); // pobieram id jednego pola "wyżej"
  12. if ($sql->num_rows() < 1) return; // jeśli nie ma takowego, przerywam
  13. $row = $sql->fetch_array();
  14. $otheritem_id = $row['id'];
  15.  
  16. $sql->query('UPDATE `###menuitems` SET `order`=`order`-1 WHERE `id`='.$menuitemid); // zamieniam ordery
  17. $sql->query('UPDATE `###menuitems` SET `order`=`order`+1 WHERE `id`='.$otheritem_id);
  18. }
  19. ?>


Specyfikacja rezultatu:

Działa bez zarzutu.

Specyfikacja problemu:

Cholera, tutaj są aż cztery zapytania x)

Pytanie:

Ma ktoś pomysł, jak zamienić te order'y szybciej i za pomocą mniejszej liczby zapytań?
heaven
Witam.

Wykombinowałem kod przy pomocy ktorego robi sie to jednym zapytaniem. Nic nie psuje jesli np. wiersz juz jest na samej gorze a ty mimo wszystko bedziesz go chcial przesunac wyzej. Nic nie musisz sprawdzac. Przetestowyany i dziala ale ... tak niefortunnie wybrales pole 'order' slowo zarezerwowane ze trzeba je brac w cudzyslowia i trzeba robic dodatkowe aliasy. Dla mojej testowej tabeli (categories) kod wyglada nastepujaco.

  1. UPDATE categories c, ( (SELECT categories_id, ord.categories_order
  2. FROM categories, (SELECT categories_order
  3. FROM categories WHERE categories_order < (SELECT categories_order
  4. FROM categories WHERE categories_id = 6
  5. LIMIT 1 )
  6. ORDER BY categories_order DESC LIMIT 1 ) AS ord
  7. WHERE categories_id = 6)
  8. UNION
  9. (SELECT c1.categories_id, ord.categories_order
  10. FROM categories c1, (SELECT categories_order
  11. FROM categories WHERE categories_id = 6) AS ord
  12. WHERE c1.categories_order < (SELECT categories_order
  13. FROM categories WHERE categories_id = 6
  14. LIMIT 1 )
  15. ORDER BY c1.categories_order DESC LIMIT 1 )) AS nowe_dane
  16. SET c.categories_order = nowe_dane.categories_order
  17. WHERE c.categories_id = nowe_dane.categories_id


Wszystkie wystapienia "6" musisz zastapic swoja zmienna $menuitemid (przyklad jest dla kategorii o id 6)
categories -> ###menuitems
categories_id -> id
categories_order -> order

Przerobka nie powinna sprawiac klopotow a tylko troche gimnastyki bo tak jak mowilem musisz wszystko apostrofowac (tzn nazwa tabeli i kolumna order) a przy moich nazwach nie trzeba

pozdrawiam

Ps. jesli mialbys problem z przerobka daj znac, pomoge
jesli bys nei mogl odnalesc sie w tym kodzie to moge wytlumaczyc krok po kroku
rolnix
Heaven, jesteś moim pozytywnym bożyszczem :*

W mojej wersji wygląda to w ten sposób:

  1. UPDATE ###menuitems c, ( (SELECT `id`, ord.`order`
  2. FROM ###menuitems, (SELECT `order`
  3. FROM ###menuitems WHERE `order` < (SELECT `order`
  4. FROM ###menuitems WHERE `id` = '.$menuitemid.'
  5. LIMIT 1 )
  6. ORDER BY `order` DESC LIMIT 1 ) AS ord
  7. WHERE `id` = '.$menuitemid.')
  8. UNION
  9. (SELECT c1.`id`, ord.`order`
  10. FROM ###menuitems c1, (SELECT `order`
  11. FROM ###menuitems WHERE `id` = '.$menuitemid.') as ord
  12. WHERE c1.`order` < (SELECT `order`
  13. FROM ###menuitems WHERE `id` = '.$menuitemid.'
  14. LIMIT 1 )
  15. ORDER BY c1.`order` DESC LIMIT 1 )) AS nowe_dane
  16. SET c.`order` = nowe_dane.`order`
  17. WHERE c.`id` = nowe_dane.`id`


I działa bez zarzutu. Dziękuję bardzo smile.gif. Jednak, jeśli miałbyś minimalne chęci, mógłbyś przynamniej pobieżnie wytłumaczyć, jakim mechanizmem to działa? Paru rzeczy nie mogę skojarzyć, a chcę się doskonalić :]
heaven
powiedzmy ze mamy tabele categories (przytocze ta moja wersje smile.gif a w niej wiersze
Kod
categories_id  |  categories_order
----------------------------------
...            |  ...
1              |  13
2              |  14
...            |  ...

chodzi nam o zamienienie przy tych dwoch wierszach wartosci kolumny categories_order

mozna to zapisac tak:
  1. UPDATE categories c, (SELECT 1 AS categories_id, 14 AS categories_order
  2. UNION
  3. SELECT 2 AS categories_id, 13 AS categories_order) AS nowe_dane
  4. SET c.categories_order = nowe_dane.categories_order
  5. WHERE c.categories_id = nowe_dane.categories_id

czyli:
  1. SELECT 1 AS categories_id, 14 AS categories_order
  2. UNION
  3. SELECT 2 AS categories_id, 13 AS categories_order

tworzy nam to tabele z 2 wierszami
Kod
categories_id  |  categories_order
----------------------------------
1              |  14               *(1)
2              |  13               *(2)

i nastepnie to wpada w update i dla categories_id = 1 jest ustawiane categories_order na 14 a dla categories_id = 2 categories_order ustawiane na 13

czyli mamy update 2 wierszy za pomoca jednego zapytania ale potrzebowalibysmy jeszcze jednego zapytania na pobranie danych do tego zapytania
my chcemy zrobic zapytanie gdzie jedyna wiadoma jest id kategori ktora chcemy przesunac w gore o jeden czyli catgories_id='2'

na poczatku utworzymy zapytanie ktore zwroci wiersz *(2) z powyzszej tabeli czyli id = 2 i order = 13
  1. SELECT categories_id, ord.categories_order
  2. FROM categories, (SELECT categories_order
  3. FROM categories WHERE categories_order < (SELECT categories_order
  4. FROM categories WHERE categories_id = 2
  5. LIMIT 1 )
  6. ORDER BY categories_order DESC LIMIT 1 ) AS ord
  7. WHERE categories_id = 2

zaczynajac od zapytania najbardziej zagniezdzonego
  1. SELECT categories_order
  2. FROM categories WHERE categories_id = 2
  3. LIMIT 1

to zwroci nam categories_order dla naszej przesowanej kategori id = 2 czyli 14 i to wstawiam do zapytania mniej zagniezdzonego
  1. SELECT categories_order
  2. FROM categories WHERE categories_order < (14)
  3. ORDER BY categories_order DESC LIMIT 1

to znowy zwroci nam najwiesza wartosc categories_order mniejsza od 14 czyli 13

czyli ogolnie cale zapytanie wyglada mniejwiecej tak
  1. SELECT categories_id, ord.categories_order
  2. FROM categories, 13 AS ord
  3. WHERE categories_id = 2

no i to da wynik w postaci tabeli z jednym wierszem
Kod
categories_id  |  categories_order
----------------------------------
2              |  13

polowe mamy teraz jeszcze wiesz z id = 1 i order = 14
  1. SELECT c1.categories_id, ord.categories_order
  2. FROM categories c1, (SELECT categories_order
  3. FROM categories WHERE categories_id = 2) AS ord
  4. WHERE c1.categories_order < (SELECT categories_order
  5. FROM categories WHERE categories_id = 2
  6. LIMIT 1 )
  7. ORDER BY c1.categories_order DESC LIMIT 1

a wiec zaczynajac od zapytania zagniezdzonego
  1. SELECT categories_order
  2. FROM categories WHERE categories_id = 2

to zwraca nam categories_order = 14 wiec zapytanie wyglada teraz tak
  1. SELECT c1.categories_id, ord.categories_order
  2. FROM categories c1, 14 AS ord.categories_order
  3. WHERE c1.categories_order < (SELECT categories_order
  4. FROM categories WHERE categories_id = 2
  5. LIMIT 1 )
  6. ORDER BY c1.categories_order DESC LIMIT 1

  1. SELECT categories_order
  2. FROM categories WHERE categories_id = 2
  3. LIMIT 1

to zwroci nam categories_order dla naszej przesowanej kategori id = 2 czyli 14 (w sumie to nie potrzebenie bo bylo to zrobione w poprzednim zapytaniu i mozna to znalesc pod aliasem ord.categories_order wiec na koncu napisze krotsza wersje tego calego zapytania)
  1. SELECT c1.categories_id, ord.categories_order
  2. FROM categories c1, 14 AS ord.categories_order
  3. WHERE c1.categories_order < 14
  4. ORDER BY c1.categories_order DESC LIMIT 1

to cale zapytanie zwroci nam id wiersza ktory ma byc zamieniony z naszym wybranym wierszem czyli 1 i categories_order 14
Kod
categories_id  |  categories_order
----------------------------------
1              |  14

teraz te 2 selecty laczymy za pomoca union i wstawiamy do update i to wszystko smile.gif
mam nadzieje ze te wypociny chociaz troche sa zrozumiale

tak wiec wersja troszke krotsza
  1. UPDATE categories c, ( (SELECT categories_id, ord.categories_order
  2. FROM categories, (SELECT categories_order
  3. FROM categories WHERE categories_order < (SELECT categories_order
  4. FROM categories WHERE categories_id = 6
  5. LIMIT 1 )
  6. ORDER BY categories_order DESC LIMIT 1 ) AS ord
  7. WHERE categories_id = 6)
  8. UNION
  9. (SELECT c1.categories_id, ord.categories_order
  10. FROM categories c1, (SELECT categories_order
  11. FROM categories WHERE categories_id = 6) AS ord
  12. WHERE c1.categories_order < ord.categories_order
  13. ORDER BY c1.categories_order DESC LIMIT 1 )) AS nowe_dane
  14. SET c.categories_order = nowe_dane.categories_order
  15. WHERE c.categories_id = nowe_dane.categories_id
rolnix
Jestem niezmiernie wdzięczny za wytłumaczenie... i pełen podziwu za to, że Ci się chciało 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.