Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> [IF][CASE]![JOIN] Złączenie z kilku tabel z warunkami
vokiel
post 23.07.2008, 11:59:25
Post #1





Grupa: Zarejestrowani
Postów: 2 592
Pomógł: 445
Dołączył: 12.03.2007

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


Witam. Mam problem ze złączeniem danych z kilku tabel, w zależności od wartości danych w jednej kolumnie.
Dokładniej:
mam 3 tabele:
PRODUCTS
| ID | NAME | .... |

AWARDS
| ID | NAME |...|

OPERATIONS
| ID | .... | ACTION | PRODUCT |

I teraz pojawia się problem bo mamy ogólnie 3 dostępne akcje:
1 - standard
2 - produkt
3 - nagroda

czyli przykładowe rekordy będą wyglądać tak:
OPERATIONS
| 1 | ... | 1 | NULL |
| 2 | ... | 2 | 2 |
| 3 | ... | 3 | 1 |
| 4 | ... | 2 | 1 |
| 5 | ... | 1 | NULL |

W wyniku chciałbym otrzymać:
QUERY
| 1 | ... | 1 | NULL |
| 2 | ... | 2 | AWARD 2 NAME |
| 3 | ... | 3 | PRODUCT 1 NAME |
| 4 | ... | 2 | AWARD 1 NAME |
| 5 | ... | 1 | NULL |

Zatem JOIN chyba odpada, bo nie będzie wiedział z której tabeli dołączać.
Przydałby się IF a może bardziej CASE

skleciłem coś takiego z case, ale nie działa tak jak trzeba, w kolumnie NAME dostaje wszędzie NULL:
  1. SELECT
  2. `OPERATIONS`.`ID` ,
  3. `OPERATIONS`.`ACTION` ,
  4. CASE
  5. WHEN `OPERATIONS`.`ACTION` =2 THEN `AWARDS`.`NAME`
  6. WHEN `OPERATIONS`.`ACTION` =3 THEN `PRODUCTS`.`NAME`
  7. END AS `NAME` ,
  8. FROM `OPERATIONS` LEFT OUTER JOIN `AWARDS` ON `OPERATIONS`.`PRODUCT` = `AWARDS`.`ID`
  9. LEFT OUTER JOIN `PRODUCTS` ON `OPERATIONS`.`PRODUCT` = `PRODUCTS`.`ID`
  10.  
  11. WHERE `OPERATIONS`.`ID` >10 LIMIT 0, 30


A może najlepszym rozwiązaniem byłoby dodanie kolumn dla AWARDS i PRODUCTS?

Ten post edytował vokiel 23.07.2008, 13:07:06


--------------------
Go to the top of the page
+Quote Post
ddiceman
post 23.07.2008, 20:42:29
Post #2





Grupa: Zarejestrowani
Postów: 326
Pomógł: 121
Dołączył: 23.07.2008
Skąd: Wrocław

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


Pisze z glowy, wiec wymaga to sprawdzenia

  1. SELECT operations.id, ..., IF(awards.id IS NULL, products.name, award.name) AS name FROM operations LEFT JOIN products ON operations.action = 2 AND operations.product = product.id
  2. LEFT JOIN awards ON operations.action = 3 AND operations.product = awards.id
  3. WHERE operations.id > 10 LIMIT 0, 30;
Go to the top of the page
+Quote Post
vokiel
post 24.07.2008, 08:36:27
Post #3





Grupa: Zarejestrowani
Postów: 2 592
Pomógł: 445
Dołączył: 12.03.2007

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


Działające poprawnie zapytanie (bez uproszczeń):
  1. SELECT `PROG_POINTS`.`ID` , `PROG_POINTS`.`CODE` , CONCAT( `PROG_USR`.`IMIE` , ' ', `PROG_USR`.`NAZWISKO` ) AS `USR` , `PROG_POINTS`.`POINTS` , `PROG_POINTS`.`ACTION` , `PROG_POINTS`.`DATE_TIME` , CONCAT( `PROG_USR_JB`.`NAME` , ' ', `PROG_USR_JB`.`SURNAME` ) AS `USR_JB` ,
  2. CASE
  3. WHEN `PROG_POINTS`.`ACTION` =3
  4. THEN `PROG_AWARD_LIST`.`AWARD_NAME`
  5. WHEN `PROG_POINTS`.`ACTION` =2
  6. THEN `PROG_PRODUCTS`.`NAME`
  7. END AS `AWARD_NAME`
  8. FROM `PROG_USR` , `PROG_POINTS`
  9. LEFT JOIN `PROG_AWARD_LIST` ON ( `PROG_POINTS`.`ACTION` =3 AND `PROG_POINTS`.`PRODUCT_ID` = `PROG_AWARD_LIST`.`ID` )
  10. LEFT JOIN `PROG_PRODUCTS` ON ( `PROG_POINTS`.`ACTION` =2 AND `PROG_POINTS`.`PRODUCT_ID` = `PROG_PRODUCTS`.`ID` )
  11. LEFT OUTER JOIN `PROG_USR_JB` ON `PROG_POINTS`.`PASSED_BY` = `PROG_USR_JB`.`ID`
  12. WHERE `PROG_POINTS`.`USR_ID` = `PROG_USR`.`ID`
  13. ORDER BY `PROG_POINTS`.`DATE_TIME` DESC LIMIT 0, 100


Musi być CASE zamiast IF, bo IF(awards.id IS NULL, products.name, award.name) skutkuje, że wyświetla zawsze, tylko products.name, zmieniłem na początku na IS NOT NULL, żeby wyświetlał, jak pole nie będzie puste (ale wtedy niezależnie od parametru ACTION wyświetlał własnie tylko "products.name". Zatem skorzystałem z CASE, który okazał się tu idealnym rozwiązaniem.

@ddiceman bardzo pomocny okazał się Twój LEFT JOIN:
  1. LEFT JOIN products ON operations.action = 2 AND operations.product = product.id

Dzięki smile.gif


--------------------
Go to the top of the page
+Quote Post
osiris
post 24.07.2008, 12:39:00
Post #4





Grupa: Zarejestrowani
Postów: 121
Pomógł: 15
Dołączył: 19.07.2007

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


Wydaje mi sie ze wydajniejszym zapytaniem byloby:
  1. (
  2. SELECT `PROG_POINTS`.`ID` , `PROG_POINTS`.`CODE` , CONCAT( `PROG_USR`.`IMIE` , ' ', `PROG_USR`.`NAZWISKO` ) AS `USR` , `PROG_POINTS`.`POINTS` , `PROG_POINTS`.`ACTION` , `PROG_POINTS`.`DATE_TIME` AS czas, CONCAT( `PROG_USR_JB`.`NAME` , ' ', `PROG_USR_JB`.`SURNAME` ) AS `USR_JB` , `PROG_AWARD_LIST`.`AWARD_NAME` AS `AWARD_NAME`
  3. FROM `PROG_USR` INNER JOIN `PROG_POINTS` ON (`PROG_POINTS`.`USR_ID` = `PROG_USR`.`ID` AND `PROG_POINTS`.`ACTION` = 3)
  4. LEFT JOIN `PROG_AWARD_LIST` ON `PROG_POINTS`.`PRODUCT_ID` = `PROG_AWARD_LIST`.`ID`
  5. LEFT JOIN `PROG_USR_JB` ON `PROG_POINTS`.`PASSED_BY` = `PROG_USR_JB`.`ID`
  6. )
  7. UNION ALL
  8. (
  9. SELECT `PROG_POINTS`.`ID` , `PROG_POINTS`.`CODE` , CONCAT( `PROG_USR`.`IMIE` , ' ', `PROG_USR`.`NAZWISKO` ) AS `USR` , `PROG_POINTS`.`POINTS` , `PROG_POINTS`.`ACTION` , `PROG_POINTS`.`DATE_TIME` AS czas , CONCAT( `PROG_USR_JB`.`NAME` , ' ', `PROG_USR_JB`.`SURNAME` ) AS `USR_JB` , CASE WHEN `PROG_POINTS`.`ACTION` = 2 THEN `PROG_PRODUCTS`.`NAME` ELSE NULL END AS `AWARD_NAME`
  10. FROM `PROG_USR` INNER JOIN `PROG_POINTS` ON (`PROG_POINTS`.`USR_ID` = `PROG_USR`.`ID` AND `PROG_POINTS`.`ACTION` <> 3)
  11. LEFT JOIN `PROG_PRODUCTS` ON `PROG_POINTS`.`PRODUCT_ID` = `PROG_PRODUCTS`.`ID`
  12. LEFT JOIN `PROG_USR_JB` ON `PROG_POINTS`.`PASSED_BY` = `PROG_USR_JB`.`ID`
  13. )
  14. ORDER BY czas DESC LIMIT 0, 100


Wg mnie powyzszy sposob jest wydajnieszy bo wykonujesz jedno zlaczenie mniej, a operacja zlaczenia jest najbardziej obciazajaca.

Pozdrawiam

Ten post edytował osiris 24.07.2008, 13:15:47
Go to the top of the page
+Quote Post
vokiel
post 24.07.2008, 12:56:49
Post #5





Grupa: Zarejestrowani
Postów: 2 592
Pomógł: 445
Dołączył: 12.03.2007

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


Dzięki za zainteresowanie smile.gif
Niestety, w Twoim przykładzie, zapytanie wybiera tylko te rekordy, dla których `PROG_POINTS`.`ACTION` = 3 sad.gif


--------------------
Go to the top of the page
+Quote Post
osiris
post 24.07.2008, 13:15:58
Post #6





Grupa: Zarejestrowani
Postów: 121
Pomógł: 15
Dołączył: 19.07.2007

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


Poprawilem
Go to the top of the page
+Quote Post
vokiel
post 24.07.2008, 14:39:43
Post #7





Grupa: Zarejestrowani
Postów: 2 592
Pomógł: 445
Dołączył: 12.03.2007

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


No i działa biggrin.gif
Trudno mi teraz mierzyć wydajność obu zapytań, bo na stan dzisiajszy w bazie jest jeszcze niewiele rekordów.
Pierwsze wyniki

Twoje zapytanie:
- 0.0035 s
- 0.0021 s
- 0.0020 s

Poprzednie zapytanie:
- 0.0021 s
- 0.0014 s
- 0.0009 s

Zatem na razie trudno wskazać szybsze. Zobacze z czasem jak przybędzie danych.
Wielkie dzięki za pomoc!
pozdrawiam


--------------------
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: 21.06.2025 - 23:15