SQL - problem ze zrozumieniem podzapytań.

0

Cześć, mam do was ogromną prośbę. Czy jest możliwość żeby ktoś w łopatologiczny sposób wyjaśnił mi jak w praktyce najlepiej robić podzapytania ? Jak się do tego zabrać i na co zwracać uwagę ? Poniżej wklejam wycinek z bazy i może na tym przykładzie ktoś by umiał wytłumaczyć ? Bo wydaję mi się, że właśnie w tym zapytaniu należy użyć podzapytania.
Treść: Wykaz studentów (Imie, Nazwisko) studiujących na Programach (KodPs) wydziału Fizyki (KodW = 'FIZ') na semestrze '2015/2016 Zima' wraz z ich średnią ocen na tym semestrze.
http://pokazywarka.pl/nybd16/

Z góry dziękuję za odpowiedź :)

0

Można z podzapytaniami, można bez

0

Jeśli na diagramie przejdziesz "po kreskach" od jednej tabeli do drugiej, to wtedy nie musisz stosować podzapytan. Ale możesz bo czasem się okazuje, że pozdzapytania mogą być wydajniejsze.

0

Chodzi o coś takiego : http://pokazywarka.pl/mqzxip/ tylko od jakiej oceni zacząć ? I jak obliczyć tą średnią ocen ?

Zrobiłem coś takiego, tylko bez ocen.

 SELECT S.Imie, S.Nazwisko 
FROM Student S
inner join programosoby PO ON S.IdO = PO.IdO 
inner join etaposoby EO ON PO.IdPo = EO.IdPo
Where exists(Select SM.NazwaS FROM Semestr SM left join etaposoby
			EO1 ON SM.KodS = EO1.KodS 
            Where SM.NazwaS = '15/16 Zima' AND EO1.IdPo = EO.IdPo)
            AND exists(select PS.KodPs FROM programstudiow PS
						Where PS.NazwaPS = 'WCY' and PS.KodPs = PO.KodPs)
Group by S.Imie,S.Nazwisko;
0
 Select s.imie, s.nazwisko, avg(o.wartosc) from student s inner join programosoby po on (s.ido=po.ido) inner join programstudiow ps on (po.kodps=ps.kodps)  inner join etaposoby eo on (po.idpo=eo.idpo) inner join semestr se on (eo.kods=se.kods) inner join ocena o on (s.ido=o.ido and eo.ideo=o.ideo) where ps.kodw='fiz' and se.nazwas='2015/2016 zima' group by s.ido, s.imie, s.nazwisko
1

@lightinside:

mysql> SELECT COUNT(*) FROM sample_data;
+----------+
| COUNT(*) |
+----------+
|        8 |
+----------+
1 row in set (0.00 sec)

mysql> SELECT COUNT(*) FROM sample_data WHERE id = (SELECT SLEEP(1) );
+----------+
| COUNT(*) |
+----------+
|        0 |
+----------+
1 row in set (8.00 sec)

Nie jestem specem od baz danych, ale dla mnie to powód do tego, żeby w większości przypadków unikać subquery. W MySQL 5.6 wprowadzili subquery materialization, ale przed 5.6 to co chwila miałem z tym problem.

0

Dzięki, ale jak rozpoznać kiedy można zastosować podzapytanie a kiedy nie warto go stosować i poprawny wynik wyjdzie tylko ze złączeń join ?

0

@pawlo00: raczej takiej sytuacji nie będziesz miał. Jeżeli robisz subquery jako kolumna, to zazwyczaj łączysz po jakimś kluczu w warunku where. To samo można zrobić przenosząc subquery do joina, a where do warunku ON, więc chyba zawsze można przepisać jedno na drugie, ale nie jestem przewien. Przeniesienie w tym wypadku będzie też szybsze, ponieważ subquery wykona się raz.

Możliwe, że są jakieś przypadki, które mi teraz nie przychodzą do głowy.

Joiny są zazwyczaj szybsze niż podzapytania.

Odsyłam do When to use SQL sub-queries versus a standard join?

0

Oj uwierz mi będę miał :) Widać, że na innej uczelni studiuje :)

0

@pawlo00: Teraz przyszedł mi do głowy taki use case.

Jeżeli chcesz ograniczyć wyniki zapytania, to musisz subquery przesunąć z selecta do joina, bo inaczej nie dostaniesz takich samych wyników.

Załóżmy, że masz takie zapytanie

SELECT
  `t`.`id`,
  `t`.`agreement_number`,
  `t`.`agreement_date`,
  `tas`.`amount_scheduled`
FROM `transaction` AS `t`
  LEFT JOIN (
    SELECT
      `ts`.`transaction_id`,
      SUM(tsp.amount) AS `amount_scheduled`
    FROM `transaction_schedule` AS `ts`
      LEFT JOIN `transaction_schedule_payment` AS `tsp` ON ts.id = tsp.transaction_schedule_id
    GROUP BY `ts`.`transaction_id`
  ) AS `tas` ON t.id = tas.transaction_id

Zapytanie wybiera:

  • id
  • numer umowy
  • datę zawarcia umowy
  • sumę wszystkich wartości z harmonogramu wpłat połączonego z dana umową

Jeżeli zostawimy jest w takiej formie w jakiej jest, czyli z left joinem, to możemy bez problemu nałożyć warunek where, który ograniczy nam wynik zapytania do transakcji, gdzie suma = 100 000.

SELECT
  `t`.`id`,
  `t`.`agreement_number`,
  `t`.`agreement_date`,
  `tas`.`amount_scheduled`
FROM `transaction` AS `t`
  LEFT JOIN (
    SELECT
      `ts`.`transaction_id`,
      SUM(tsp.amount) AS `amount_scheduled`
    FROM `transaction_schedule` AS `ts`
      LEFT JOIN `transaction_schedule_payment` AS `tsp` ON ts.id = tsp.transaction_schedule_id
    GROUP BY `ts`.`transaction_id`
  ) AS `tas` ON t.id = tas.transaction_id
-- teraz dostaniemy mniej wyników
WHERE tas.amount_scheduled = 100000

Jeżeli przeniesiemy left joina do selecta, to już nie będziemy mogli ograniczyć wyników zapytania.

SELECT
  `t`.`id`,
  `t`.`agreement_number`,
  `t`.`agreement_date`,
  (SELECT SUM(tsp.amount) AS `amount_scheduled`
   FROM `transaction_schedule` AS `ts`
     LEFT JOIN `transaction_schedule_payment` AS `tsp` ON ts.id = tsp.transaction_schedule_id
   -- dodanie tego where'a w tym miejscu nie ma sensu
   -- WHERE SUM(tsp.amount) = 100000
   WHERE ts.transaction_id = t.id
   GROUP BY `ts`.`transaction_id`) AS `amount_scheduled`
FROM `transaction` AS `t`
-- to nie zadziała
-- WHERE tas.amount_scheduled = 100000
-- to można dodać i wynik będzie taki sam, ale having jest wolniejsze
--  HAVING amount_scheduled = 10000

Nie wiem czy nie walnąłem w tych zapytaniach jakiegoś typo, ale ideę powinieneś zrozumieć :) Jeszcze można się kłócic, że jak masz subquery w select to możesz uzyć having, czyli taki where, ale możesz uzyc aliasu.. tylko że to jest masakrycznie wolne.

WHERE restricts the result set before returning rows and HAVING restricts the result set after bringing all the rows.

0

W sumie mam coś takiego, mam takie tabele Wydział --.> Przemiot <--PrzemiotZewnetrzny--> UczelniaZewnetrzna i do przedmiotu idzie jeszcze jedna tabela (<--GrupaPrzedmiotow). Na szybko robione, wybaczcie :D
I mam wypisać Listę przedmiotow (KodPs,Nazwa) , aktywnych (Status w przedmiocie = 3) prowadzonych przez UW z liczbą wszystkich programów studiów, na których te przedmioty występują. Brane są pod uwagę tylko te przedmioty które są przypisane do grupy przedmiotów "jakaś grupa" lub nie są przypisane do żadnej grupy.
Zrobiłem to tak, że podzapytanie dałem w warunku where i w tym podzapytaniu wypisałem przedmioty należące do danej grupy, lecz prowadzący powiedział, że składnia jest zła i 0 pkt :D

0

Napisałem tak:

 SELECT P.KodPr , P.Nazwa , COUNT(DISTINCT PS.KodPs) LProgram
FROM UczelniaZewnetrzna UZ INNER JOIN PrzedmiotZewnetrzny PZ ON UZ.IdU = PZ.IdU
INNER JOIN Przedmiot P ON PZ.KodPr = P.KodPr
INNER JOIN Wydzial W ON P.KodW = W.KodW
INNER JOIN ProgramStudiow PS ON W.KodW = PS.KodW
WHERE P.Status = 3 AND UZ.Nazwa = 'Uniwersytet Warszawski'
AND EXISTS(SELECT 1 
FROM Przedmiot P1 INNER JOIN GrupaPrzedmiotow GP ON P1.IdGP = GP.IdGP
WHERE GP.Nazwa = 'jakas grupa' OR GP.Nazwa IS NULL
AND P1.KodPr = P.KodPr)
GROUP BY P.KodPr , P.Nazwa

1 użytkowników online, w tym zalogowanych: 0, gości: 1