Fórum Root.cz

Hlavní témata => Vývoj => Téma založeno: Ħαℓ₸℮ℵ ␏⫢ ⦚ » 13. 11. 2019, 10:51:29

Název: SQL dotázek
Přispěvatel: Ħαℓ₸℮ℵ ␏⫢ ⦚ » 13. 11. 2019, 10:51:29
Jak z SQL databáze záznamy odpovídající podmínce? Názorný příklad : tabulka lidí(bez normalizovaní), která vlastní auta (pro jednoduchost značky). Chci vybrat lidi, kteří vlastní alespoň Nissan a audi (nebo alternativně řádky tabulky patřící této osobě).
jmeno;auto
Petr;Citroen
Petr;Audi
Petr;Nissan

Ludvik;NIssan
Jarda;Nissan;
Jarda;Citroen
Karel;Audi
Filip;Audi

Obejde se to bez subselectu?
Případně to jde udělat nějak přes WHERE auto="Nissan" AND/OR auto="Audi" -> group by jmeno -> having(count(auto))>=2 ? Je to čisté řešení?

Jak se nazývají podobné dotazy(úlohy)? Mám tušení že nějak komponované/korelované....
Název: Re:SQL dotázek
Přispěvatel: gill 13. 11. 2019, 11:07:40
určitě bez subselectu, použij self join
Název: Re:SQL dotázek
Přispěvatel: Kit 13. 11. 2019, 11:36:53
Kód: [Vybrat]
SELECT DISTINCT jmeno FROM Karta
    JOIN Karta AS K2 ON Karta.jmeno=K2.jmeno
    WHERE Karta.auto='Nissan' AND K2.auto='Audi';

Z toho je vidět, jak je normalizace důležitá.
Název: Re:SQL dotázek
Přispěvatel: Filip Jirsák 13. 11. 2019, 12:26:50
Je to čisté řešení?
Není, čisté řešení je ty tabulky normalizovat.

Případně to jde udělat nějak přes WHERE auto="Nissan" AND/OR auto="Audi" -> group by jmeno -> having(count(auto))>=2 ?
S AND to určitě nebude fungovat, nebudete mít v jednom řádku Nissan i Audi. COUNT(auto) vám vrátí 2 i v případě, že tam budete mít dva záznamy s Nissan. Tj. pokud je podmínkou, že má Nissan i Audi, je ta podmínka špatně.

Začněte ale tou normalizací. A pak bych použil SELECT FROM seznam_osob WHERE EXIST (vlastní Audi) AND (vlastní Nissan) (pokud je podmínkou, že vlastní oba). Je to přesný popis toho, co chcete získat, a optimalizátor dotazů si s tím nejspíš poradí.

Název: Re:SQL dotázek
Přispěvatel: Standa Blábol 15. 11. 2019, 09:28:27
Tohle je klasicka vazba M:N, takze aby to bylo ciste, potrebujes 3 tabulky

Person (id [PK], name), Car (id [PK]), Person2Car(person_id, car_id)  PK oba sloupce + 2x foreign key na obe strany

select distinct p.* from Person p
  join Person2Car pc on p.id=pc.person_id
  join Car c on c.id on c.id=pc.car_id
  where c.name in ('Nissan','Audi')
Název: Re:SQL dotázek
Přispěvatel: gill 15. 11. 2019, 10:09:29
on se zeptal na dotaz a tady mu místo jednoduché odpovědi radí překopat celé schéma.
Název: Re:SQL dotázek
Přispěvatel: Tomas-T 15. 11. 2019, 20:08:12
Když jedna z otázek autora je "Je to čisté řešení?", tak se není čemu divit.  :)
Název: Re:SQL dotázek
Přispěvatel: gill 15. 11. 2019, 21:16:05
Když jedna z otázek autora je "Je to čisté řešení?", tak se není čemu divit.  :)

on se ptal jestli je ten jeho dotaz čisté řešení.
Název: Re:SQL dotázek
Přispěvatel: Ħαℓ₸℮ℵ ␏⫢ ⦚ » 18. 11. 2019, 09:43:19
Není, čisté řešení je ty tabulky normalizovat.
Ach jo. zase Jiráskovo hnidopišství o detailech, které vůbec nehrají roli. Tak si představte, že místo Petr je 123456 a místo Nissan 9876, zase někdo našel detail.

Jak je tady normalizace důležitá?


A dovolím si rozšířit dotaz: co když bude , že má 4 vybraná auta. To se bude dělat 3násobný join? Bude to efektivní?
Název: Re:SQL dotázek
Přispěvatel: gill 18. 11. 2019, 10:14:00
Bude to efektivní, pokud máš nad těmi sloupci index.
Název: Re:SQL dotázek
Přispěvatel: Miroslav Šilhavý 18. 11. 2019, 10:47:48
Záleží na množství. V malých počtech bude navržené řešení podobně efektivní, udělá se index nad textem.
Ve větších množstvích by vazba samozřejmě pomohla, protože tabulka hodnot by byla unique not null a indexoval by se pouze číselný typ. Join by to pak jednoznačně výkonově vyhrál.
Název: Re:SQL dotázek
Přispěvatel: Filip Jirsák 18. 11. 2019, 13:54:50
Ach jo. zase Jiráskovo hnidopišství o detailech, které vůbec nehrají roli. Tak si představte, že místo Petr je 123456 a místo Nissan 9876, zase někdo našel detail.
Děkuji, že jste to zkompletoval. Před čtrnácti dny jsem tu dostal vynadáno, jak si můžu dovolit psát o nenormalizované tabulce. Normalizovaná tabulka je evidentně taky špatně. Jestli on nebude problém spíš s těmi, co mají na všechno svá jediná správná řešení…

Jak je tady normalizace důležitá?
Tak, že umožní o problému strukturovaně přemýšlet. Potřebuju získat záznam o jednom člověku? Výborně, mám tady tabulku lidí, z té si ten jeden záznam vytáhnu. Potřebuju si k němu vytáhnout auta? Výborně, mám tady spojovací tabulku. A najednou nemusí tazatel řešit self-joiny, čímž odpadá polovina jeho zmatení a to zadání nejspíš dokáže vyřešit sám.

A dovolím si rozšířit dotaz: co když bude , že má 4 vybraná auta. To se bude dělat 3násobný join? Bude to efektivní?
V tom dotazu je, že vlastní Nissan a Audi. Pokud budete mít každé auto ve zvláštním řádku tabulky, bez vícenásobného JOINu se neobejdete. Leda by vaše databáze podporovala pole a vy jste z těch řádků nejprve udělal pole.  Nicméně když řešíte efektivitu, JOINy umí optimalizovat každá databáze; vytvářet z více záznamů jeden záznam s polem hodnot asi nebude zrovna věc, kterou by databáze optimalizovaly.
Název: Re:SQL dotázek
Přispěvatel: Miroslav Šilhavý 18. 11. 2019, 14:01:10
V tom dotazu je, že vlastní Nissan a Audi. Pokud budete mít každé auto ve zvláštním řádku tabulky, bez vícenásobného JOINu se neobejdete. Leda by vaše databáze podporovala pole a vy jste z těch řádků nejprve udělal pole.  Nicméně když řešíte efektivitu, JOINy umí optimalizovat každá databáze; vytvářet z více záznamů jeden záznam s polem hodnot asi nebude zrovna věc, kterou by databáze optimalizovaly.

Ale houby, jeden join na tabulku s auty.
Buďto (a to bývá nejčastější) vyhovuje navrátit více řádků ke každému jménu (co řádek, to jedna značka), nebo
značky můžete agregovat do pole, to zvládne i MySQL.
Název: Re:SQL dotázek
Přispěvatel: Filip Jirsák 18. 11. 2019, 17:11:12
Ale houby, jeden join na tabulku s auty.
Buďto (a to bývá nejčastější) vyhovuje navrátit více řádků ke každému jménu (co řádek, to jedna značka), nebo
značky můžete agregovat do pole, to zvládne i MySQL.
Nějak tu nevidím ten SELECT, asi se vám při vkládání komentáře ztratil.
Název: Re:SQL dotázek
Přispěvatel: Miroslav Šilhavý 18. 11. 2019, 17:55:05
Nějak tu nevidím ten SELECT, asi se vám při vkládání komentáře ztratil.

Asi narážíte na to, že joiny budou dva. Máte pravdu.

SELECT jmeno
FROM tbl1
LEFT JOIN tbl_vazba USING (vazba_left)
INNER JOIN tbl_hodnoty USING (vazba_right)
WHERE tbl_hodnoty.auto IN ('Audi', 'Nissan')
HAVING count(*) = 2
GROUP BY jmeno


Podtržené having když vynecháte, tak získáte požadovaný operátor Audi OR Nissan.
Pokud ho doplníte, získáte operátor Audi AND Nissan.

(Pro operátor OR by šlo použít DISTINCT, ale výkonnostně je totožné, takže je výhodnější si to abstrahovat do jednoho dotazu a HAVING přidat podle potřeby)

Select je jen schematicky, prosím nepeskujte mě, pokud neprojde, píšu je z ruky.
Název: Re:SQL dotázek
Přispěvatel: Filip Jirsák 18. 11. 2019, 20:57:25
Jak pak asi dopadne tohle?
Kód: [Vybrat]
jmeno;auto
Petr;Audi
Petr;Citroen
Petr;Audi
Ludvik;Nissan
Jarda;Nissan;
Jarda;Citroen
Karel;Audi
Filip;Audi

Ale to už tu bylo zmíněno, že?

Select je jen schematicky, prosím nepeskujte mě, pokud neprojde, píšu je z ruky.
Nicméně to, že HAVING je podmínka vztahující se na záznamy po seskupení (na rozdíl od WHERE), a proto se píše až za GROUP BY, byste mohl vědět, i když to píšete z ruky. Pořadí WHERE, GROUP BY, HAVING a ORDER BY je dané tím, jak se (jakoby) postupně aplikují na sadu záznamů.
Název: Re:SQL dotázek
Přispěvatel: Miroslav Šilhavý 18. 11. 2019, 21:03:48
Jak pak asi dopadne tohle?
Kód: [Vybrat]
jmeno;auto
Petr;Audi
Petr;Citroen
Petr;Audi
Ludvik;Nissan
Jarda;Nissan;
Jarda;Citroen
Karel;Audi
Filip;Audi

Ale to už tu bylo zmíněno, že?

Pokud se Vám toto v datech vyskytuje, pak musíte změnit podmínku na HAVING count(DISTINCT vazba_right).
Smysluplnější by bylo přidat UNIQUE INDEX ON tbl_vazba (vazba_left, vazba_right).
Název: Re:SQL dotázek
Přispěvatel: mudrlant 18. 11. 2019, 21:14:52
A nejde to třeba jednodušeji pomoci průniku?

(SELECT jmeno FROM auta WHERE auto='Nissan')
INTERSECT
(SELECT jmeno FROM auta WHERE auto='Audi')

Jak z SQL databáze záznamy odpovídající podmínce? Názorný příklad : tabulka lidí(bez normalizovaní), která vlastní auta (pro jednoduchost značky). Chci vybrat lidi, kteří vlastní alespoň Nissan a audi (nebo alternativně řádky tabulky patřící této osobě).
Název: Re:SQL dotázek
Přispěvatel: Miroslav Šilhavý 18. 11. 2019, 21:27:13
A nejde to třeba jednodušeji pomoci průniku?

Ano, to by šlo, ale např. MySQL INTERSECT neumí.
(Jen pro doplnění: INTERSECT by sloužil pro AND, pro OR by se zase použil UNION)

Nevýhodou tohoto řešení by byla komplikovanost, pokud by se hledal průnik (nebo union) pro více hodnot než pro vzorové dvě. Dotaz by pak (ne)úměrně narůstal.
Název: Re:SQL dotázek
Přispěvatel: Tomáš Jura 18. 11. 2019, 22:14:49
Kód: [Vybrat]
select jmeno
from T
where auto in ('Nissan','Audi')
group by jmeno
having count(*) = 2 -- nebo > 1 pokud hledáte vlastníka alespoň jednoho z vozidel
Vše ostatní bude mít větší algoritmickou složitost. ( join, intersection = zakuklený join ).

Nevím co je to za divnou mánii posuzovat složitost selectu podle existence subselectu. Subselect je regulérní logická konstrukce ( stejně jako závorky v algebře čísel ). I dotaz s několika subselecty může být jednoduchý na vyhodnocení a naopak pouhý jeden join (s disjunktivní join podmínkou) může být totální zabiják.
Název: Re:SQL dotázek
Přispěvatel: Rob Ros 19. 11. 2019, 09:08:07
Kód: [Vybrat]
select jmeno
from T
where auto in ('Nissan','Audi')
group by jmeno
having count(*) = 2 -- nebo > 1 pokud hledáte vlastníka alespoň jednoho z vozidel

Jsem jen blba uklizecka, ale tohle podle me vubec neresi situaci, kdy ma uzivatel napr. 2 Nissany a zadne Audi.
Název: Re:SQL dotázek
Přispěvatel: Miroslav Šilhavý 19. 11. 2019, 09:58:23
Kód: [Vybrat]
select jmeno
from T
where auto in ('Nissan','Audi')
group by jmeno
having count(*) = 2 -- nebo > 1 pokud hledáte vlastníka alespoň jednoho z vozidel

Jsem jen blba uklizecka, ale tohle podle me vubec neresi situaci, kdy ma uzivatel napr. 2 Nissany a zadne Audi.

Na to stačí přepsat na HAVING count(DISTINCT *).