Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Diskuze: SQL dotaz přes více tabulek

V předchozím kvízu, Online test znalostí PHP, jsme si ověřili nabyté zkušenosti z kurzu.

Aktivity
Avatar
Michal Kuba
Tvůrce
Avatar
Michal Kuba:28.4.2018 21:49

Ahoj!

Mám v dotazu dvě tabulky propojené přes JOIN.
V té první je několik údajů o areálu (samozřejmě každý má své jedinečné ID), ve druhé mám potom jednotlivé dráhy - jeden areál má logicky více drah. Takže v tabulce drah sou nějaké informace jako délka, obtížnost a také ID areálu, ve kterém daná dráha je (jde tam o delku a obtížnost její, nějaký popisy a názvy drah neřeším).

Dostávám se k podstatě problému. Vybírám podle hodnot s filtru vyhovující areály.. Zaseknu se ale na problému, kdy někdo zadá minimální délku dráhy a zároveň i specifickou obtížnost (obtížnosti jsou 1 - 4). Chci vyhledat všechny areály, které mají alespoň jednu dráhy větší než zadanou délku a zároveň ten areál má i jednu dráhu dané obtížnosti, JENŽE: nechci najít jen dráhy, které odpovídají minimální délce a taky obtížnosti - chci najít i takové areály, které mají více drah a jedna z nich splňuje minimální délku a druhá obtížnost (ale ta zase nemusí mít požadovanou délku).

Zkoušel jsem to různě kombinovat přes OR nebo AND, ale nic. Potom celé výsledky jsou GROUPované podle ID AREÁLU.

Zde kus kódu dotazu, který ale nevrací správné výsledky:

SELECT destinations.*
FROM `destinations`
JOIN `slope` AS  s ON s.destination_id = destinations.id
WHERE destinations.status = 'allow' AND  ( s.slope_type_id = 4 )  AND s.length >= 1500
GROUP BY destinations.id

Takhle to logicky najde jen ty dráhy, které mají slope_type_id = 4 a délku větší než 1500.

Předem díky za rady, už jsem opravdu bezradný.. Snad existuje nějaké východisko :)

 
Odpovědět
28.4.2018 21:49
Avatar
Jakub Švasta
Lektor
Avatar
Odpovídá na Michal Kuba
Jakub Švasta:28.4.2018 22:13

Já bych to udělal třeba takhle:

SELECT * FROM `destinations`
WHERE destinations.status = 'allow'
        AND EXISTS (SELECT 1 FROM `slope` AS s WHERE s.destination_id = destinations.id AND s.slope_type_id = 4)
        AND EXISTS (SELECT 1 FROM `slope` AS s WHERE s.destination_id = destinations.id AND s.length >= 1500)
Editováno 28.4.2018 22:14
Akceptované řešení
+20 Zkušeností
+2,50 Kč
Řešení problému
 
Nahoru Odpovědět
28.4.2018 22:13
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:2.5.2018 15:44

Vsechny drahy, ktere splnuji podminku. Primarni tabulka, jsou drahy. K nim chces pridat dalsi udaje.
areal (id_areal, nazev)
draha (id_draha, id_areal, nazev, delka, obtiznost)

SELECT
    a.nazev AS draha,
    b.nazev AS areal,
    a.delka
FROM
    draha a
    LEFT JOIN areal b ON b.id_areal=a.id_areal
WHERE
    a.delka > 15
    AND a.obtiznost > 4

Alespon jedna podminka

a.delka > 15
OR a.obtiznost > 4

A pokud te zajima pouze jmeno arealu, tak se da pouzit DISTINCT k odfiltrovani duplicit, ale v SELECTU musis mit jen jmeno arealu, b.nazev. Priklady zapisu se daji najit googlem...

SELECT DISTINCT SalesTerritoryCountry
FROM [AdventureWorksDW2012].[dbo].[DimSalesTerritory]
ORDER BY SalesTerritoryCountry
Editováno 2.5.2018 15:45
 
Nahoru Odpovědět
2.5.2018 15:44
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:2.5.2018 15:47

Jeste kombinace s tim allow. To si snadno prepises do vlastniho, ale ty zavorky jsou dulezite.

b.povoleno = 1
AND (a.delka > 15 OR a.obtiznost > 4)
 
Nahoru Odpovědět
2.5.2018 15:47
Avatar
Michal Kuba
Tvůrce
Avatar
Odpovídá na Peter Mlich
Michal Kuba:2.5.2018 16:13

A když později budu upravovat ještě i že se dá filtrovat jiný parametr areálu, typicky jaký tam je vlek, tedy podobné kritéria jako pro dráhu.. To už budou vlastně dvě stejný věci který bych potřeboval jako primární tabulku, ale zároveň propojenou a jen v jednom dotazu. .:(

 
Nahoru Odpovědět
2.5.2018 16:13
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:3.5.2018 8:09

Pozn. Mozna by bylo dobre uz na zacatku rici, ze se jedna o horsky areal s lyzarskou drahou, vlekem. Pro lepsi predstavu.

Nechapu otazku. Do toho sql dotazu muzes stale doplnovat. Tam je hlavne dulezite napsat spravne podminku.
Se na to divas obracene, mozna.
Ty mas arealy, ktere maji vleky a drahy. Drahy jsou ruzne obtizne, ruzne dlouhe. Nechapu, Areal nebo draha je povolena, nebo oboji. Budu uvazovat oboji.
Takze, tebe zajima:

  • zda existuje draha, ktera je povolena
  • zda ta draha vyhovuje dalsim parametrum, vlek, obtiznostm delka
  • do jakeho arealu patri
  • a zda ten areal je povoleny (otevreny; rekneme, ze areal muze mit nejake drahy zakazane nebo byt zavreny jako celek, ale drahy muzou byt otevrene)

No, a presne takto stavis sql dotaz

SELECT ... FROM drahy LEFT JOIN areal -- vyber drahy, pripoj k nim info o arealu
WHERE
drahy.povoleno = true AND
areal.povoleno = true AND
(drahy.delka>15 AND drahy.obtiznost<4 AND drahy.vlek=true) -- vsude AND, zavorka nemusi byt

Nebo chces, aby platili nektere parametry spolecne...

((drahy.delka>15 OR drahy.obtiznost<4) AND drahy.vlek=true) -- u OR zavorku mit musis

Pouzivani and, or, je 1. hodina programovani, boolenovska algebra, stredni, mozna uz zakladni skola, vs take. Pripadne se to uci jeste v matematice, stredni skola, vs take. To jsou takove ty silene podminky "Kdyz prsi, tak se neco vykona". Soucasne jsou to v programovani podminky IF (a==b and c==d).

Vysledkem dotazu je seznam drah. Treba 3 radky. Co s tim dal chces provest, je na tobe.

Muzes udelat jen SELECT DISTINCT areal.id FROM ... a cele to uzavrit do dalsiho selectu, kde k arealu pripojis dalsi info. Nebo areal.id vyuzijes pro vytahnuti vsech drah arealu. Nejen tech, ktere vyhovuji podmince.

SELECT a.id, a.name, b.name
FROM  drahy a ON ...
    LEFT JOIN areal b ON ...
WHERE b.id IN (SELECT DISTINCT areal.id FROM ...)
ORDER BY ...

Vyber vsechny drahy, ktere maji areal.id z tabulky sql dotazu v zavorce.
Nebude to trvat dlouho, protoze si vysledek dotazu v zavorce ulozi do pameti.
Pokud bys tu zavorku pouzil do FROM, tak je nutne dat te zavorce alias. Aby s ni slo pracovat. Ono, i sql bude psat error.

SELECT** x.id** ...
FROM (SELECT DISTINCT areal.id FROM ...) **x**
    LEFT JOIN drahy z ON z.id_areal = **x.id_areal
**WHERE ...
Editováno 3.5.2018 8:10
 
Nahoru Odpovědět
3.5.2018 8:09
Avatar
katrincsak
Člen
Avatar
Odpovídá na Michal Kuba
katrincsak:3.5.2018 13:06

Úplně přesně nechápu problém, ale třeba ti moje rada dokáže pomoct a rozšířit možnosti řešení daného problému. V případě že si potřebuješ dotáhnout data na příč tabulkám a podle toho pak dále s tím pracovat, nebo se může jednat o tu samou tabulku, ale není možné to vytáhnout v jednom dotaze, tak doporučím.

V selektu můžeš udělat již hotový dotaz a vytáhnout si do něj údaj jaký chceš, samozřejmě se musí jednat o jeden výsledek s jedním parametrem dle mnou uvedeného příkladu. Těch selektů můžeš udělat kolik chceš i v rámci stejné tabulky. Tak snad se ti to bude hodit. Někdy to ušetří spoustu času.

SELECT d.*, (SELECT s.destination_id FROM slope s WHERE neco AND neco) AS nove_id FROM destinations d WHERE nove_id =1234;
 
Nahoru Odpovědět
3.5.2018 13:06
Avatar
katrincsak
Člen
Avatar
katrincsak:3.5.2018 13:48

A napsal jsem to špatně. Na konci nemůžeš udělat nove_id = 1234, ale již " WHERE d.neco = nove_id". Chtěl jsem to uvést jako příklad a nepřemýšlel již u toho.

 
Nahoru Odpovědět
3.5.2018 13:48
Avatar
Michal Kuba
Tvůrce
Avatar
Odpovídá na Peter Mlich
Michal Kuba:8.5.2018 17:18

Ano, poprvé jsem to nenapsal úplně přesně.. Nicméně ať zkouším různě kombinace a přidávám nějaké SQL podmínky, tak furt nemám kýžený výsledek..

Jde o to, že mám dejme tomu 200 areálů, každý má třeba v rozmezí 2-15 nějakých drah (jedna je 1000m dlouhá a nejlehčí, další 785m dlouhá a obtížně nejtěžší atd..)..

Do filtru zadám, že chci dráhy delší jak 1000m a středně těžký.. Jenže když udělám dotaz s ANDy, tak to hledá JEN dráhy, které splňují oba požadavky.. Avšak já chci najít ty areály, které mají alespoň jednu dráhu delší jak 1000m (ale může mít jinou obtížnost) a aspoň jednu, co má střední obtížnost (ale délka může být nižší jak 1000m)..

Když tam dám ORy, tak to ale potom najde areály, které některou podmínku nesplňují - třeba jim chybí obtížnost správná.. Další věcí co možná jsem zapomněl říct nebo to nevyznělo správně je ta, že filtrovat se dá podle více obtížností zároveň, zadám si třeba dvě, takže chci výsledný areál, který má obě obtížnosti a ještě alespoň jednu dráhu delší než 1000m

Ani DISTINCT mi nepomohl :(

 
Nahoru Odpovědět
8.5.2018 17:18
Avatar
Michal Kuba
Tvůrce
Avatar
Michal Kuba:8.5.2018 22:26

Tak jsem to nakonec nějak namastil a zdá se funkčně. Trochu víc jsem pogooglil a nakonec jsem dotaz doplnil o tento kus podmínky a funguje to :)

AND EXISTS (SELECT 1 FROM slope s WHERE s.destination_id = d.id AND s.slope_type_id = 4)

takže se mi dostane toho, že všechny požadované typy obtížnosti které jsou vybrány :)

Díky všem co poradili :)

 
Nahoru Odpovědět
8.5.2018 22:26
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:9.5.2018 11:54

drahy obtiznosti 1 nebo 3 nebo delky 1000

obtiznost IN (1,3) OR delka>1000
obtiznost=1 OR obtiznost=3 OR delka>1000

drahy obtiznosti 1 nebo 3 a soucasne delky 1000

obtiznost IN (1,3) AND delka>1000
(obtiznost=1 OR obtiznost=3) AND delka>1000
obtiznost=1 AND delka>1000 OR obtiznost=3 AND delka>1000
(obtiznost=1 AND delka>1000) OR (obtiznost=3 AND delka>1000)

drahy obtiznosti 1 a 3 a soucasne delky 1000

obtiznost IN (1,3) AND delka>1000
(obtiznost=1 OR obtiznost=3) AND delka>1000

delší jak 1000m a středně těžký (rekneme 3 je stredne tezka)

obtiznost IN (3) AND delka>1000
obtiznost=3 AND delka>1000

Pokud mas vic filtru, tak bych sel do zavorkoveho reseni

(delka>2000) OR (obtiznost=1 AND delka<100)

Chci s delkou 2000 (pro dospele bezky nebo sjezd) a soucasne obtiznost 1 s delkou<100 (pro deti). Ted by to porovnalo parametry drahy a zda vyhovuje aspon jedne zavorce, tak ji prida do vystupu

Ten DISTINCT dela to, ze odfiltruje duplicity. Kdyz pouzijes ten muj sql dotaz, do podminek WHERE das drahy, ale v SELECT budes mit jen jmeno arealu, tak, kdyz ma areal 5 drah, 3 vyhovuji, tak by tam byl areal 3x.

SELECT arealy.name
FROM drahy LEFT JOIN arealy ON drahy.id_areal=arealy.id_atealy
WHERE (drahy.delka>2000) OR (drahy.obtiznost=1 AND drahy.delka<100)

Tady to ti vrati treba 5x stejny areal, ale kdyz to zmenis na SELECT DISTINCT , tak by to melo dat jen jeden areal.

 
Nahoru Odpovědět
9.5.2018 11:54
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 11 zpráv z 11.