Diskuze: UNION v podotazu + přístup k proměnné aktuálního řádku
V předchozím kvízu, Online test znalostí SQL a databází, jsme si ověřili nabyté zkušenosti z kurzu.
Ještě pro upřesnění klauzuli directory_client
.*
nepoužívám, vím, že to je nevalidní s GROUP BY, chtěl jsem jen zkrátit
kód. Klidně tam může být
directory_client
.name
.
Zde jen doplním zjednodušenou strukturu dat:
CLIENTS
+------------------+
| NAME | ID |
+------------------+
| Company 001 | 1 |
+------------------+
| Company 002 | 2 |
+------------------+
BRANCH OFFICES
+----------------+
| ID | ID_CLIENT |
+----------------+
| 1 | 1 |
+----------------+
| 2 | 2 |
+----------------+
PERSONS
+----------------+-----------+
| ID | ID_CLIENT | ID_BRANCH |
+----------------+-----------+
| 1 | 1 | 0 |
+----------------+-----------+
| 2 | 0 | 2 |
+----------------+-----------+
EVENTS
+----------------+-----------+-----------+
| ID | ID_CLIENT | ID_BRANCH | ID_PERSON |
+----------------+-----------+-----------+
| 1 | 1 | 0 | 0 |
+----------------+-----------+-----------+
| 2 | 0 | 2 | 0 |
+----------------+-----------+-----------+
| 3 | 0 | 0 | 1 |
+----------------+-----------+-----------+
'o zjištění poslední uskutečněné události'
SELECT id_event -- vyber id eventu
FROM event -- z tabulky eventy
ORDER BY date DESC -- , kterou seradis podle datumu
LIMIT 1 -- a vyber 1 radek
Pokud k tomu chces dalsi informace, tak je pripojis joinem, pokud nejsou v tabulce eventu.
SELECT a.id_event, b.neco, c.neco -- zmena
FROM event a -- mala zmena
LEFT JOIN tab2 b ON b.id_tab2=a.id_tab2 -- zmena
LEFT JOIN tab3 c ON c.id_tab3=a.id_tab3 -- zmena, tady to treba muzes propojit na tab2 c.id_tab2=b.id_tab2
LEFT JOIN tab4 d ON d.id_tab4=a.id_tab4 -- zmena
ORDER BY a.date DESC -- mala zmena
LIMIT 1
zyxzyx:2.10.2018 21:32
Ahoj,
díky za radu, ale to bohužel není ono.
Jde o to, že když koukáme na seznam firem (klientů), tak by nás zajímalo, kdy naposled se tam něco dělo. Ale výběr událostí musí zahrnovat vše, i podřízené záznamy (pobočky, osoby).
Někde musí být klauzule WHERE, která určí, jakého klienta se to
týká.
Každý SELECT v mém dotazu toto zohledňuje a je jiný, protože např.
existují události připojené k osobě, která je připojena k pobočce a ta
je zase připojena ke klientovi a ID toho klienta je rozhodující. Proto jsem
volil UNION, abych zohlednil všechny možné kombinace výběru, které se
událostí daného klienta týkají.
Ok, zkusim upravit ten puvodni dotaz. Treba na neco prijdu.
Treba bys mohl dat nejaky priklad i vystupni tabulky z tech, ktere jsi napsal.
Ale asi to jinak nez UNIONem nepujde, pokud tipuji spravne, co asi chces.
Michal Štěpánek:3.10.2018 8:26
Trošku si protiřečíš. Buď se chceš podívat na poslední záznam, pak je to tak, jak ti radil Peter Mlich, nebo chceš vidět poslední záznam podle nějaké podmínky. Pak to bude zase dost podobně, jen tam nebude to "ORDER", ale bude tam WHERE podmínka. Např.
WHERE (FirmaID = nějaké ID firmy) OR (PobočkaID = nějaké ID pobočky) OR ...
zyxzyx:3.10.2018 8:57
Ahoj,
díky za odpověď. Nemyslím, že bych si protiřečil. Jde o to, že vytvářím seznam firem, znám tedy jen ID firmy na aktuálním řádku. Jak bych měl zjistit ID pobočky, kterých navíc může být nekonečné množství a vložit jej do WHERE? To platí i u osob, které mohou být napojeny přimo k firmě, ale také k pobočkám. Jednotlivé údálosti pak mohou být napojeny jak na osobu, pobočku i přímo na firmu, jak jsem se snažil ukázat v té struktuře. Snažím se tedy zjistit poslední událost týkající se FIRMY včetně jejich podřízených záznamů.
Podle mě je UNION správné řešení, jen jaksi není v poddotazu dostupné aktuální ID firmy. Hláška "Unknown column CURR_CLIENT_ID". Pokud ho dosadím přímo do dotazu, funguje to správně, ale to pak mám na každém řádku stejný výsledek, který se vztahuje jen na záznam s ID, který jsem vložil ručně, což není žádoucí.
Pak jsem zkoušel použít stejný dotaz, ale nepoužil jsem poddotaz, vynechal jsem klauzuli FROM a aliasy. Vše by fungovalo až na řazení podle data "DESC". Zobrazoval se první a nikoli poslední záznam.
Michal Štěpánek:3.10.2018 9:00
Která data v době pokládání dotazu máš k dispozici? ID firmy? ID člověka? nebo co?
Trochu jsem to preformatoval jednim programem. Na chyby v kodu nehled. vidim tyhle problemy
- Treba eliminovat joiny ktere nepotrebujes, Left joiny, pokud je nepotrebujes, tak je pouzij az na konec
curr_client_id
je co? To je nejaky sloupec z jake tabulky? Nebo je to cislo z php, treba?
Jestli je to z tabulky, tak musis do toho vnitrniho dotazu tabulku pridat.
SELECT a.`date_from` AS `EVENT_DATE`
FROM `calendar_event` a
LEFT JOIN `list_calendar_event` b ON b.`idlist_calendar_event` = a.`list_calendar_event_idlist_calendar_event`
LEFT JOIN `directory_client` e ON ... -- zmena
--WHERE a.`directory_client_iddirectory_client` = `curr_client_id`
WHERE a.`directory_client_iddirectory_client` = e.`curr_client_id`
Nebo, mozna chces neco jineho. Mozna potrebujes selectem vytahnout nejdriv id klikentu a ty pak pouzit v In.
SELECT ... WHERE id IN (SELECT `iddirectory_client` FROM `directory_client`)
Ale, to je zbytecne, protoze ziskas stejne vsechny klienty.
3. Hele, a dokazes vysledek te tabulky ziskat treba jinym zpusobem, nekolika
dotazy? Zkus napsat ty. Z toho se to bude pak lepe skladat.
Zatim se v tom ja treba nevyznam, prilis. Ale taky jsem je amater. Neni mi
uplne jasne, jak to myslis s tim
curr_client_id. Trochu se desim predstavy, ze ty chces pouzit promennou z
vysledne tabulky do tabulek, ze kterych ji vytvaris
-- `calendar_event` a
-- `list_calendar_event` b
-- `directory_branch_office` c
-- `directory_person` d
-- `directory_client` e
-- `directory_address` f
SELECT e.*,
( e.`iddirectory_client` ) AS `CURR_CLIENT_ID`,
(
SELECT `event_date`
FROM (
SELECT a.`date_from` AS `EVENT_DATE`
FROM `calendar_event` a
LEFT JOIN `list_calendar_event` b ON b.`idlist_calendar_event` = a.`list_calendar_event_idlist_calendar_event`
WHERE a.`directory_client_iddirectory_client` = `curr_client_id`
UNION ALL
SELECT a.`date_from` AS `EVENT_DATE`
FROM `calendar_event` a
LEFT JOIN `list_calendar_event` b ON b.`idlist_calendar_event` = a.`list_calendar_event_idlist_calendar_event`
INNER JOIN `directory_branch_office` c ON c.`iddirectory_branch_office` = a.`directory_branch_office_iddirectory_branch_office`
INNER JOIN `directory_client` e ON e.`iddirectory_client` = c.`directory_client_iddirectory_client`
INNER JOIN `directory_address` f ON f.`directory_branch_office_iddirectory_branch_office` = c.`iddirectory_branch_office`
WHERE c.`directory_client_iddirectory_client` = `curr_client_id`
UNION ALL
SELECT a.`date_from` AS `EVENT_DATE`
FROM `calendar_event` a
LEFT JOIN `list_calendar_event` b ON b.`idlist_calendar_event` = a.`list_calendar_event_idlist_calendar_event`
INNER JOIN `directory_person` d ON d.`iddirectory_person` = a.`directory_person_iddirectory_person`
INNER JOIN `directory_client` e ON e.`iddirectory_client` = d.`directory_client_iddirectory_client`
WHERE d.`directory_client_iddirectory_client` = `curr_client_id`
UNION ALL
SELECT a.`date_from` AS `event_date`
FROM `calendar_event` a
LEFT JOIN `list_calendar_event` b ON b.`idlist_calendar_event` = a.`list_calendar_event_idlist_calendar_event`
INNER JOIN `directory_person` d ON d.`iddirectory_person` = a.`directory_person_iddirectory_person`
INNER JOIN `directory_branch_office` c ON c.`iddirectory_branch_office` = d.`directory_branch_office_iddirectory_branch_office`
INNER JOIN `directory_client` e ON e.`iddirectory_client` = c.`directory_client_iddirectory_client`
INNER JOIN `directory_address` f ON f.`directory_branch_office_iddirectory_branch_office` = c.`iddirectory_branch_office`
WHERE e.`iddirectory_client` = `curr_client_id`
) AS `event_tab`
ORDER BY `event_date` DESC
LIMIT 0, 1
)
AS
`last_event_all`
zyxzyx:3.10.2018 9:01
Ahoj, díky za snahu. Níže jsem odpověděl Michalovi, tak se mrkni.
Těžko se to takto popisuje .
Mohl bych ti poslat soubor, kde mám ukázková data včetně struktury.
zyxzyx:3.10.2018 9:05
Dělám výpis firem. Takže jen ID firmy a chci do seznamu přidat sloupec s poslední uskutečněnou událostí, která se ale může vztahovat i k podřízeným záznamům firmy (pobočkám, osobám).
Aha, mozna vim, co chces.
id = SELECT id FROM tab1
row[0] = SELECT1 ... WHERE id=id[0] + SELECT2 ... WHERE id=id[0] ...
row[1] = SELECT1 ... WHERE id=id[1] + SELECT2 ... WHERE id=id[1] ...
To, ceho jsem se desil.
Nic, no, nekdy pozdeji promyslim....
zyxzyx:3.10.2018 9:19
Ne, to by bylo moc i na mě .
Zkusím ještě jedno vysvětlení.
Kdybych jednotlivé dotazy, které mám spojeny pomocí UNION dal zvlášť, tak
výsledek budou 4 další sloupce viz níže. A já bych to potřeboval
sjednotit do jednoho sloupce a vybrat jen tu nejnovější událost. Mohl bych
to udělat pak v PHP, ale čekám požadavek na možnost řazení a
filtrování, tak bych to rád nechal na databázi.
[LAST_EVENT_CLIENT] => 2018-09-11 09:30:00
[LAST_EVENT_BRANCH] => 2018-10-11 18:00:00
[LAST_EVENT_PERSON] => 2018-09-20 13:30:00
[LAST_EVENT_BRANCH_PERSON] => 2018-09-25 17:00:00
zyxzyx:3.10.2018 11:35
Zkusil jsem tvé řešení s těmi aliasy, ale má to sjený háček, který mě trápí: Column not found: 1054 Unknown column 'curr_client_id'
Peter Mlich:3.10.2018 14:38
Jo, aliasy neres. To jsem jen zkusil upravil, abych se v tom vyznal, kde konci ktery select.
Mozna by ti stacilo...
(
SELECT id_event, id_client, date
FROM event
ORDER BY date DESC -- nebo mozna asc, nevim, jak funguje ted group, zda da prvni nebo posledni radek
) AS tab1 -- vytahnout tabulku, seradit
GROUP BY id_client -- a pak to teprve grupovat
SELECT
`directory_client`.`iddirectory_client` AS `CURR_CLIENT_ID`
, MAX(`EVENT_TAB`.`EVENT_DATE`) AS `EVENT_DATE`
FROM
`directory_client`
INNER JOIN (
SELECT
`calendar_event`.`date_from` AS `EVENT_DATE`
, `calendar_event`.`directory_client_iddirectory_client` AS `CURR_CLIENT_ID`
FROM
`calendar_event`
UNION ALL
SELECT
`calendar_event`.`date_from` AS `EVENT_DATE`
, `directory_branch_office`.`directory_client_iddirectory_client` AS `CURR_CLIENT_ID`
FROM
`calendar_event`
INNER JOIN `directory_branch_office` ON `directory_branch_office`.`iddirectory_branch_office` = `calendar_event`.`directory_branch_office_iddirectory_branch_office`
INNER JOIN `directory_client` ON `directory_client`.`iddirectory_client` = `directory_branch_office`.`directory_client_iddirectory_client`
INNER JOIN `directory_address` ON `directory_address`.`directory_branch_office_iddirectory_branch_office` = `directory_branch_office`.`iddirectory_branch_office`
UNION ALL
SELECT
`calendar_event`.`date_from` AS `EVENT_DATE`
, `directory_person`.`directory_client_iddirectory_client` AS `CURR_CLIENT_ID`
FROM
`calendar_event`
INNER JOIN `directory_person` ON `directory_person`.`iddirectory_person` = `calendar_event`.`directory_person_iddirectory_person`
INNER JOIN `directory_client` ON `directory_client`.`iddirectory_client` = `directory_person`.`directory_client_iddirectory_client`
UNION ALL
SELECT
`calendar_event`.`date_from` AS `EVENT_DATE`
, `directory_client`.`iddirectory_client` AS `CURR_CLIENT_ID`
FROM
`calendar_event`
INNER JOIN `directory_person` ON `directory_person`.`iddirectory_person` = `calendar_event`.`directory_person_iddirectory_person`
INNER JOIN `directory_branch_office` ON `directory_branch_office`.`iddirectory_branch_office` = `directory_person`.`directory_branch_office_iddirectory_branch_office`
INNER JOIN `directory_client` ON `directory_client`.`iddirectory_client` = `directory_branch_office`.`directory_client_iddirectory_client`
INNER JOIN `directory_address` ON `directory_address`.`directory_branch_office_iddirectory_branch_office` = `directory_branch_office`.`iddirectory_branch_office`
) AS `EVENT_TAB` ON `EVENT_TAB`.`CURR_CLIENT_ID` = `directory_client`.`iddirectory_client`
GROUP BY
`directory_client`.`iddirectory_client`
zyxzyx:4.10.2018 9:06
Díky za odpověď, ale také to buhužel nefunguje. Poddotaz nemůže vracet
dva sloupce a také, ač je to divné, i přes použití
MAX(EVENT_TAB
.EVENT_DATE
) AS EVENT_DATE
)
vrací dotaz více řádků. Zkoušel jsem to modifikovat, ale opět jsem
narazil na to předání poddotazu aktuální ID klienta. Dotaz vracel na všech
řádcích stejnou hodnotu.
plelovsky:4.10.2018 9:22
Poddotaz může vracet sloupců kolik chceš, pokud se připojí pomocí
JOINu v klauzuli FROM - pak funguje stejně jako tabulka. Nemůže vracet více
sloupců, pokud ho používáš v seznamu sloupců v klauzuli SELECT místo
jednoho sloupce.
Dotaz by měl vracet nejvyšší datum pro každou hodnotu
directory_client
.iddirectory_client
. Pokud chceš
jedno konkrétní ID klienta, musíš přidat podmínku
WHERE directory_client
.iddirectory_client
=
<hodnota>
před GROUP BY
plelovsky:4.10.2018 9:25
Poddotazu žádné ID klienta předávat nemusíš. Poddotaz vrací hodnoty ID klienta a datum, které posbírá z těch UNIONů - jejich logiku neřeším. Hlavní dotaz to pak zgrupije podle ID klienta a pro každé ID vrátí nejvyšší datum.
zyxzyx:4.10.2018 9:48
Dotaz jsem zjednodušil pro přehlednost.
V této podobě je výsledek chyba: "Cardinality violation: 1242 Subquery
returns more than 1 row".
SELECT
`directory_client`.`iddirectory_client` AS `CURR_CLIENT_ID`
, (
SELECT MAX(`EVENT_TAB`.`EVENT_DATE`)
FROM `directory_client`
INNER JOIN (
SELECT
`calendar_event`.`date_from` AS `EVENT_DATE`
, `calendar_event`.`directory_client_iddirectory_client` AS `CURR_CLIENT_ID`
FROM
`calendar_event`
) AS `EVENT_TAB` ON `EVENT_TAB`.`CURR_CLIENT_ID` = `directory_client`.`iddirectory_client`
WHERE `directory_client`.`iddirectory_client` = `CURR_CLIENT_ID`
GROUP BY `directory_client`.`iddirectory_client`
) AS `LAST_EVENT_ALL`
plelovsky:4.10.2018 10:30
Samozřejmě že poddotaz vrací více než 1 žádek, protože v něm
joinuješ maximální hodnotu data se všemi záznamy v
directory_client
. Takto ho nemůžeš použít místo sloupce v
klauzuli SELECT.
Komu není rady, tomu není pomoci.
zyxzyx:4.10.2018 10:43
Ale já ho práve potřebuju použít jako sloupec. To snad z mého dotazu vyplynulo. Prostě dohledat poslední událost ke každému řádku ve výpisu klientů, aby to zohlednilo i podřízené záznamy.
plelovsky:4.10.2018 10:50
Řešení úkolu "dohledat poslední událost ke každému řádku ve výpisu
klientů, aby to zohlednilo i podřízené záznamy" neznamená nutně, že
musí být nějaký poddotaz použitý jako sloupec.
Tvůj dotaz by ve výsledku vypisoval ID klienta a maximální datum. Můj
taky.
Jeste mne napadlo, jestli ty nechces pro kazdy ten select vypsat jedno datum, kazdy do extra sloupce. To by asi slo taky.
SELECT id_klient, date as date1, null, null, null ...
UNION
SELECT id_klient, null, date as date2, null, null ...
UNION
SELECT id_klient, null, null, date as date3, null ...
Pak to zgrupujes podle id_klienta a dostanes tabulku
id_klient, date1, date2, date3, null ...
Jo, pri grupovani pouzit pro select sloupcu to max(date1) ...
zyxzyx:4.10.2018 19:32
Ahoj, právě, že ne, ty 4 výsledky potřebuji sjednotit a vybrat ten nejnovější. Všechny se týkají totiž jedné firmy.
Myslím, že jsem už na to přišel, tady je zkrácený příklad: http://tpcg.io/vwtKuF
Ještě to rozšířím a otestuji na ostrých datech a napíšu sem
výsledek.
Zobrazeno 27 zpráv z 27.