Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> Problem z pobraniem ścieżki w drzewie kategorii, Problem z rekurencyjnym wywołaniem funkcji zwracającej rekordy
phpion
post 24.07.2008, 08:42:12
Post #1





Grupa: Moderatorzy
Postów: 6 070
Pomógł: 860
Dołączył: 10.12.2003
Skąd: Dąbrowa Górnicza




Witam,
mam struktruę tabeli:
p_category: id, parent_id, name
czyli w zasadzie standardzik. Chciałbym napisać funkcję, która zwróci mi ścieżkę od danej kategorii do korzenia. Mam coś takiego:
  1. CREATE OR REPLACE FUNCTION f_get_category_path(category_id integer, shop_id integer)
  2. RETURNS p_category AS $BODY$DECLARE
  3. r p_category%ROWTYPE;
  4. BEGIN SELECT * INTO r FROM p_category WHERE id=$1;
  5.  
  6. IF (r.parent_id > 0) THEN
  7. -- SELECT * FROM f_get_category_path(r.parent_id, $2);
  8. END IF;
  9.  
  10. RETURN r;
  11. END;$BODY$
  12. LANGUAGE 'plpgsql' VOLATILE
  13. COST 100;

Mam problem z rekurencyjnym wywołaniem tej funkcji. Jeśli odkomentuję linijkę w warunku otrzymuję:
Kod
ERROR:  query has no destination for result data
HINT:  If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT:  PL/pgSQL function "f_get_category_path" line 7 at SQL statement
SQL statement "SELECT * FROM f_get_category_path( $1 ,  $2 )"
PL/pgSQL function "f_get_category_path" line 7 at SQL statement

Jak to poprawnie zapisać?

pion

Ten post edytował phpion 24.07.2008, 16:31:28
Go to the top of the page
+Quote Post
Sedziwoj
post 24.07.2008, 12:59:40
Post #2





Grupa: Zarejestrowani
Postów: 793
Pomógł: 32
Dołączył: 23.11.2006
Skąd: Warszawa

Ostrzeżenie: (0%)
-----


Jak dobrze pamiętam, to PG nie lubi jak mus się wstawia SQL z parametrami... spróbuj coś w tym stylu:
  1. EXECUTE 'SELECT * FROM f_get_category_path('||r.parent_id||', '||$2||');'

Ogólnie o PREPARE:
http://www.postgresql.org/docs/current/sta...ql-prepare.html

Do tego moim zadaniem powinieneś użyć LOOP, będzie to moim zdaniem lepsze rozwiązanie, skoro jedynie będzie jedna gałąź rekurencji do tego z jednym elementem do zapamiętania.
Trzeba zakładać, że nie pojawi się błąd pętli, bo to od razu zabije bazę (nie zależnie czy algorytm będzie rekurencyjny czy iteracyjny).


--------------------
Algorytmy w PHP, czy ktoś o tym słyszał?
Dlaczego tak mało kobiet programuje? ponieważ nie zajmują się głupotami.
Go to the top of the page
+Quote Post
phpion
post 24.07.2008, 13:58:26
Post #3





Grupa: Moderatorzy
Postów: 6 070
Pomógł: 860
Dołączył: 10.12.2003
Skąd: Dąbrowa Górnicza




Dzięki za odpowiedź. Teraz ciało funkcji wygląda w ten sposób:
  1. DECLARE
  2. r p_category%ROWTYPE;
  3. BEGIN SELECT * INTO r FROM p_category WHERE id=$1;
  4.  
  5. IF (r.parent_id IS NOT NULL AND r.parent_id > 0) THEN
  6. EXECUTE 'SELECT * FROM f_get_category_path('||r.parent_id||', '||$2||');';
  7. END IF;
  8.  
  9. RETURN r;
  10. END;

Wywołanie np.
  1. SELECT * FROM f_get_category_path(7, 1);

powinno zwrócić (wg moich zamierzeń) 3 rekordy, natomiast zwraca tylko jeden (ten o id=7 czyli parametrze przekazanym w funkcji). Wygląda jakby albo nie wchodził w ogóle do warunku (co jest mało prawdopodobne bo na pewno jest spełniony) albo nie wyświetlał rezultatów kolejny wywołań. Jeśli mógłbyś mi pomóc w tej kwestii byłbym bardzo wdzięczny.
Go to the top of the page
+Quote Post
Sedziwoj
post 24.07.2008, 15:18:27
Post #4





Grupa: Zarejestrowani
Postów: 793
Pomógł: 32
Dołączył: 23.11.2006
Skąd: Warszawa

Ostrzeżenie: (0%)
-----


Może nie za piękne... Ważne w deklaracji "SETOF "
  1. CREATE OR REPLACE FUNCTION f_get_category_path(category_id integer, shop_id integer)
  2. RETURNS SETOF p_category AS $BODY$
  3. DECLARE
  4. r p_category%ROWTYPE;
  5. aid integer;
  6. BEGIN IF category_id IS NULL THEN RETURN; END IF;
  7. aid := category_id;
  8. LOOP
  9. FOR r IN SELECT * FROM p_category WHERE id = aid
  10. LOOP
  11. RETURN NEXT r;
  12. END LOOP;
  13. EXIT WHEN NOT r.parent_id > 0; -- Nie wiem czy masz null, czy może błędnie 0 w tym polu jak nie ma rodzica.
  14. aid := r.parent_id;
  15. END LOOP;
  16. RETURN;
  17. END;
  18. $BODY$
  19. LANGUAGE 'plpgsql' VOLATILE;


EDIT: parę poprawek
EDIT2: poprawiam to co napisał phpion, aby był poprawny przykład, jakby ktoś sszukał

Ten post edytował Sedziwoj 24.07.2008, 16:53:35


--------------------
Algorytmy w PHP, czy ktoś o tym słyszał?
Dlaczego tak mało kobiet programuje? ponieważ nie zajmują się głupotami.
Go to the top of the page
+Quote Post
phpion
post 24.07.2008, 16:30:16
Post #5





Grupa: Moderatorzy
Postów: 6 070
Pomógł: 860
Dołączył: 10.12.2003
Skąd: Dąbrowa Górnicza




Działa wyśmienicie. Musiałem poprawić tylko jedną linijkę:
  1. FOR r IN SELECT * FROM p_category WHERE id = aid

bo wywalało błędy. Naprawdę ogromne dzięki! smile.gif

PS: tworzę "abstrakcyjną" kategorię ROOT o id=0 i parent_id=NULL, natomiast później wszystkie kategorie główne mają ustawione parent_id=0
Go to the top of the page
+Quote Post
Sedziwoj
post 24.07.2008, 17:02:22
Post #6





Grupa: Zarejestrowani
Postów: 793
Pomógł: 32
Dołączył: 23.11.2006
Skąd: Warszawa

Ostrzeżenie: (0%)
-----


Ja zazwyczaj miałem las, Ty chcesz mieć drzewo, to już nie nazwę błędem biggrin.gif Po prostu inna koncepcja (a mi się przypomniało jak było w bazie -1 jak nie było powiązania... i tylko dla tego wspomniałem)
Ciągle mam wrażenie że da się to lepiej napisać, ale bym musiał się tym pobawić, do tej pory nie miałem potrzeby robienia takich funkcji.


--------------------
Algorytmy w PHP, czy ktoś o tym słyszał?
Dlaczego tak mało kobiet programuje? ponieważ nie zajmują się głupotami.
Go to the top of the page
+Quote Post
phpion
post 15.12.2008, 11:58:40
Post #7





Grupa: Moderatorzy
Postów: 6 070
Pomógł: 860
Dołączył: 10.12.2003
Skąd: Dąbrowa Górnicza




Odświeżam temat: mam problem z modyfikacją wyżej podanego rozwiązania. Aktualnie mam tabele:

category: id, parent_id, position
locale: id, code
category_i18n: category_id, locale_id, name

i próbuję pobrać ścieżkę kategorii czyli np. Podkategoria -> Kategoria. Stworzyłem widok:
  1. CREATE OR REPLACE VIEW shp_view_category AS SELECT a.category_id, b.parent_id, a.name, c.code AS locale_code
  2. FROM shp_category_i18n a
  3. LEFT JOIN shp_category b ON b.id = a.category_id
  4. LEFT JOIN shp_locale c ON c.id = a.locale_id;

oraz zmodyfikowałem funkcję tak by (teoretycznie) działała poprawnie:
  1. CREATE OR REPLACE FUNCTION f_get_category_path(category_id integer, locale character varying)
  2. RETURNS SETOF shp_view_category AS $BODY$DECLARE
  3. r shp_view_category%ROWTYPE;
  4. aid integer;
  5. BEGIN IF $2 IS NULL THEN
  6. RETURN;
  7. END IF;
  8.  
  9. aid := $2;
  10.  
  11. LOOP
  12. FOR r IN SELECT * FROM shp_view_category WHERE category_id=aid AND locale_code=$2
  13. LOOP
  14. RETURN NEXT r;
  15. END LOOP;
  16.  
  17. EXIT WHEN r.parent_id=0 OR r.parent_id IS NULL;
  18.  
  19. aid := r.parent_id;
  20. END LOOP;
  21.  
  22. RETURN;
  23. END;$BODY$
  24. LANGUAGE 'plpgsql' VOLATILE
  25. COST 100
  26. ROWS 1000;

Wywłouję ją:
  1. SELECT * FROM f_get_category_path(12, 'pl_PL');

Nie działa ona jednak poprawnie. W wyniku otrzymuję wszystkie rekordy z widoku, które posiadają tłumaczenia dla kategorii 12 w wersji 'pl_PL', a nie tylko te wchodzące w skład ścieżki. Co robię źle :|

Proszę o pomoc,
pion

PS: wiem, że lepiej to zrobić z użyciem RECORD a nie tworzyć specjalnie po to widok ale na razie chciałbym się uporać z tym aby to po prostu zaczęło działać...

Ten post edytował phpion 15.12.2008, 12:05:04
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 26.04.2024 - 22:13