Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [MySQL][PHP] Wyszukiwanie przepisów po składnikach
Forum PHP.pl > Forum > Przedszkole
d0m1n1k_
Witam,
szukam dobrego rozwiązania które pozwoliłoby mi na wyszukiwanie przepisów po podanych składnikach.
Ważne aby pokazywało tylko te przepisy które nie mają innych składników niż te które wprowadziłem w inpucie.

Struktura bazy danych:
(Tabela: kolumna1,...)
przepisy: id, nazwa
przepisy_skladniki: ps_id, ps_przepis, ps_skladnik

Na chwilę obecną zdołałem napisać następujące zapytania:
  1. select DISTINCT r.nazwa, r.id
  2. from
  3. przepisy r
  4. inner join przepisy_skladniki i
  5. on i.ps_przepis = r.id
  6. where i.ps_skladnik IN (15, 99)

Problem: Pokazuje przepisy które zawierają wymienione składniaki (15 i 99 to id składników o które zapytałem)

  1. select r.nazwa, r.id
  2. from
  3. przepisy r
  4. inner join przepisy_skladniki i
  5. on i.ps_przepis = r.id
  6. where i.ps_skladnik IN (15, 99)
  7. GROUP BY r.nazwa
  8. HAVING COUNT(*) = 2


Problem: Pokazuje ten przepis który ma te składniki ale i inne, a nie ten który ma tylko te wyniki.

Co robię nie tak?
Count(*) > 2?
Też nie działa poprawnie.
Bardzo proszę o pomysł, pomoc lub rozwiązanie za które po stokroć dziękuję już teraz!
trueblue
Sprawdź czy jest ok.
  1. SELECT DISTINCT p.nazwa,p.id_przepis
  2. FROM przepis AS p,przepis_skladnik AS ps
  3. WHERE p.id_przepis=ps.id_przepis
  4. GROUP BY p.id_przepis
  5. HAVING SUM(IF(ps.id_skladnik IN(1,2),1,-1))=2
Pyton_000
Albo coś takiego:
http://stackoverflow.com/a/7505147/3732803
trueblue
Pyton_000,
to zapytanie zwróci przepisy, które zawierają również składniki spoza wybranych.
Pyton_000
Nie powinno chyba że zdefiniuje warunek HAVING >= X
trueblue
Nie powinno, ale zwróci:)
Spięcie tabel jest po id_przepis, więc jeśli przepis ma 5 składników, ale 2 z nich wybrane, to i tak zostanie wyselekcjonowany do puli wyników.
mmmmmmm
trueblue
Twoje zapytanie rónież zwróci źle. Zakładając, że najważniejsze jest:
  1. HAVING SUM(IF(ps.id_skladnik IN(1,2),1,-1))=2

to dla ps.id_skladnik ={1,2,3,4} również zwróci 2 smile.gif
dla ps.id_skladnik = {1,1} także zwróci 2
trueblue
Cytat(mmmmmmm @ 3.09.2015, 12:58:53 ) *
trueblue
Twoje zapytanie rónież zwróci źle. Zakładając, że najważniejsze jest:
  1. HAVING SUM(IF(ps.id_skladnik IN(1,2),1,-1))=2

to dla ps.id_skladnik ={1,2,3,4} również zwróci 2 smile.gif
dla ps.id_skladnik = {1,1} także zwróci 2

mmmmmmm,
Dla pierwszego przykładu zwróci 0 (+1,+1,-1,-1)
Dla drugiego, tak, masz rację. Choć wiadomo jakie powinno być założenie co do tabeli.
d0m1n1k_
trueblue nie wiem czy to moja wina przepisania czy zrozumienia, ale zapytanie:
  1. SELECT DISTINCT p.nazwa, p.id
  2. FROM przepisy AS p, przepisy_skladniki AS ps
  3. WHERE p.id = ps.ps_przepis
  4. GROUP BY p.id
  5. HAVING SUM(ID(ps.ps_id IN(1,2),1-1))=2


wyrzuca wynik w phpmyadmin: #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '))=2 LIMIT 0, 30' at line 5

Poza tym zastanawiam się, jeśli chciałbym podawać mniej lub więcej niż dwa składniki (np. w przedziale od 1 do 20) to jak powinienem do tego podjeść?
Czy może to wyglądać w ten sposób:

  1. $id_wszystkichskladnikow = $_POST['skladnik1'] . ", " . $_POST['skladnik2'] . ", " ... ", " . $_POST['skladnik20'];
  2. $suma_id = "20";
  3.  
  4. select r.nazwa, r.id
  5. from
  6. przepisy r
  7. inner join przepisy_skladniki i
  8. on i.ps_przepis = r.id
  9. where i.ps_skladnik IN (" . $id_wszystkichskladnikow . ")
  10. GROUP BY r.nazwa
  11. HAVING COUNT(*) = " . $suma_id . "
  12.  


Czy jestem na dobrym tropie? Czy nadal głęboko w lesie :-///
trueblue
  1. HAVING SUM(ID(ps.ps_id IN(1,2),1-1))=2

IF nie ID

Tyle ile id składników, takie porównanie z ilością.
Dla 4-ech byłoby:
  1. HAVING SUM(IF(ps.id_skladnik IN(1,2,5,10),1,-1))=4


Pamiętaj aby w tabeli przepisy_skladniki nie mieć przypisanego kilkukrotnie tego samego składnika do przepisu (powinien być indeks unikalny na dwóch polach).
d0m1n1k_
Z tym ID/IF to mój błąd przy wprowadzaniu zapytania na forum.
W phpMyAdmin było poprawnie (IF) i błąd wystąpił przy IF'ie ten który opisałem w poprzednim poście.
Czyli ostatnia linijka powinna wyglądać następująco?:
  1. HAVING SUM(IF(ps.id_skladnik IN(" . $id_wszystkichskladnikow . "),1,-1))=" . $suma_id . "


Dobrze to rozumiem?

Co do mysql i unikalności, mam to wykonać w następujący sposób?:

  1. ALTER TABLE przepisy_skladniki
  2. ADD UNIQUE KEY `unikalnoscskladnikow` (`ps_przepis`, `ps_skladnik`)


POPRAWKA!

Kod poprawiony:

HAVING SUM(IF(ps.ps_id IN(1,2),1,-1))=2

Takie szczegóły ;-)

Ale teraz wywala mi wynik zerowy dla zapytania 15,99 a powinno pokazać dwa przepisy :-///
trueblue
Pokaż zapytanie dla id 15 i 99.

Indeks ok, jeśli te pola to id przepisu i składnika.
d0m1n1k_
Na chwilę obecną sprawdzam statyczne zapytanie, bez zmiennych.

  1. SELECT DISTINCT p.nazwa, p.id
  2. FROM przepisy AS p, przepisy_skladniki AS ps
  3. WHERE p.id = ps.ps_przepis
  4. GROUP BY p.id
  5. HAVING SUM(IF(ps.ps_id IN(15, 99),1,-1))=2


Przypomnę układ tabel:
(Przepis) przepisy: id, nazwa
(Składniki) przepisy_skladniki: ps_id, ps_przepis, ps_skladnik
trueblue
  1. SELECT DISTINCT p.nazwa, p.id
  2. FROM przepisy AS p, przepisy_skladniki AS ps
  3. WHERE p.id = ps.ps_przepis
  4. GROUP BY p.id
  5. HAVING SUM(IF(ps.ps_skladnik IN(15, 99),1,-1))=2


Pole ps_id w tabeli przepisy_skladniki nie jest potrzebne jeśli założyłeś indeks unikalny.
d0m1n1k_
Przeanalizowałem.
Mały błąd już teraz działa smile.gif

  1. SELECT DISTINCT p.nazwa, p.id
  2. FROM przepisy AS p, przepisy_skladniki AS ps
  3. WHERE p.id = ps.ps_przepis
  4. GROUP BY p.id
  5. HAVING SUM(IF(ps.ps_skladnik IN(15, 99),1,-1))=2


W ostatniej linijce zamiast ps.ps_id wykorzystałem ps.ps_skladnik


Równo to zauważyliśmy ;-)
Ale za tyle pomocy Piwo Ci się należy! I po trój kroć klik pomógł!

Ale teeeeraz. Jak już sprawę podstawową mam załatwioną, to teraz zastanawiam się nad rozwinięciem.
Jak mógłbym zmodyfikować kod aby pokazywał mi przepisy które mają jeden lub dwa składniki więcej, sortowane po tej wartości od 1 do 10 składników dodatkowych.
trueblue
Zerknij, jeśli działa, to być może da się uprościć:
  1. SELECT DISTINCT p.nazwa, p.id, (SUM(IF(ps.ps_skladnik IN(15, 99),1,-1)) AS ilosc_podstawowa,SUM(IF(ps.ps_skladnik NOT IN(15, 99),1,-1))) AS ilosc_dodatkowa
  2. FROM przepisy AS p, przepisy_skladniki AS ps
  3. WHERE p.id = ps.ps_przepis
  4. GROUP BY p.id
  5. HAVING ilosc_podstawowa=2 AND ilosc_dodatkowa>=5
  6. ORDER BY ilosc_dodatkowa
d0m1n1k_
Niestety nie działa.
Error: #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'AS ilosc_podstawowa,SUM(IF(ps.ps_skladnik NOT IN(15, 99),1,-1))) AS ilosc_dodatk' at line 1

Zaś po zmianie nawiasów dls ilosc_podstawowa i ilosc_dodatkowa, aby były osobnymi wartościami wywala mi pusty wynik.

  1. SELECT DISTINCT p.nazwa, p.id,
  2. (SUM(IF(ps.ps_skladnik IN(15, 99),1,-1))) AS ilosc_podstawowa,
  3. (SUM(IF(ps.ps_skladnik NOT IN(15, 99),1,-1))) AS ilosc_dodatkowa
  4. FROM przepisy AS p, przepisy_skladniki AS ps
  5. WHERE p.id = ps.ps_przepis
  6. GROUP BY p.id
  7. HAVING ilosc_podstawowa=2 AND ilosc_dodatkowa>=5
  8. ORDER BY ilosc_dodatkowa


Głowię się nad tym od 2h i nie mogę tego ogarnąć :-//

Jak usunę w HAVING "AND ilosc_dodatkowa>=5" pokazuje elementy które spełniają warunek podstawowy (przepisy w których są tylko wymienione składniki),
zaś ilosc_dodatkowa w obu przypadkach wynosi -2
Gdy usunę cały zwrot HAVING otrzymuję wszystkie przepisy, ułożone od spełniających do niespełniających.

Tabela przepisy_skladniki ma następujące wpisy:
  1. ps_przepis(1) = ps_skladniki(1,2,5,6)
  2. ps_przepis(2) = ps_skladniki(99,15)
  3. ps_przepis(3) = ps_skladniki(162,164,234)
  4. ps_przepis(5) = ps_skladniki(15,99)
  5. ps_przepis(6) = ps_skladniki(8,51,53,15)
  6. ps_przepis(7) = ps_skladniki(1,29)
  7. ps_przepis(8) = ps_skladniki(25,331,99,340,234,15)


Wynik po całkowitym usunięciu klauzury HAVING jest następujący:
  1. nazwa | id | id_podstawowa | id_dodatkowa
  2. przepis(5) | 5 | 2 | -2
  3. przepis(2) | 2 | 2 | -2
  4. przepis(6) | 6 | -2 | 2
  5. przepis(7) | 7 | -2 | 2
  6. przepis(8) | 8 | -2 | 2
  7. przepis(3) | 3 | -3 | 3
  8. przepis(1) | 1 | -4 | 4
trueblue
  1. (SUM(IF(ps.ps_skladnik IN(15, 99),1,0))) AS ilosc_podstawowa,
  2. (SUM(IF(ps.ps_skladnik NOT IN(15, 99),1,0))) AS ilosc_dodatkowa
d0m1n1k_
Trochę przekombinowałem, ale

po zmianie zapytania na następujące:

  1. SELECT DISTINCT p.nazwa, p.id,
  2. (SUM(IF(ps.ps_skladnik IN(15, 99),1,0))) AS ilosc_podstawowa,
  3. (SUM(IF(ps.ps_skladnik NOT IN(15, 99),1,0))) AS ilosc_dodatkowa
  4. FROM przepisy AS p, przepisy_skladniki AS ps
  5. WHERE p.id = ps.ps_przepis
  6. GROUP BY p.id
  7. HAVING ilosc_podstawowa<=2 AND ilosc_dodatkowa<=3
  8. ORDER BY ilosc_podstawowa DESC, ilosc_dodatkowa ASC


Uzyskałem wynik już bardzo satysfakcjonujący:

  1. nazwa | id | ilosc_podstawowa | ilosc_dodatkowa
  2. przepis(5) | 5 | 2 | 0
  3. przepis(2) | 2 | 2 | 0
  4. przepis(6) | 6 | 1 | 3
  5. przepis(7) | 7 | 0 | 2
  6. przepis(3) | 3 | 0 | 3


ilosc_dodatkowa<=3 zmniejszyłem, aby wyeliminowało mi jakikolwiek przepis ;-)
Zastanawiam się jeszcze nad zmianą klauzury:
  1. HAVING ilosc_podstawowa<=2 AND ilosc_dodatkowa<=3

na
  1. HAVING ilosc_dodatkowa<=3

bo obecny efekt jest identyczny ale nie wiem czy jest to dobre rozwiązanie.
trueblue
Wynik jest identyczny, ale to akurat przypadek.
Nie zawsze tak musi być.
Jeśli będziesz mieć przepis z czterema wybranymi składnikami i trzema innymi dodatkowymi, to przepis będzie spełniał tylko jeden z tych warunków (ten krótszy).
d0m1n1k_
To może zmniejszyć wymaganie na:
  1. HAVING ilosc_podstawowa >= 1 AND ilosc_dodatkowa <= 3

lub
  1. $half = ceil($id_suma / 2);

  1. HAVING ilosc_podstawowa >= " . $half . " AND ilosc_dodatkowa <= 3


Wtedy warunkiem byłoby żeby minimum połowa (nie mniej niż 1) wymaganych składników była w przepisie,
a sortowanie ORDER BY ilosc_podstawowa DESC, ilosc_dodatkowa ASC dawałoby palmę pierwszeństwa dla najbardziej dostosowanych.
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.