Autor Zpráva
quatzael
Profil
Mám již, zde někde zmiňovaný zápis:

SELECT *
 FROM a
 JOIN b
 ON a.order_id = b.order_id
 WHERE a.user_id = 12 AND a.status = 'on' ORDER BY a.datum DESC LIMIT 10 , 20;

Problém je, že když mám v tabulce a dva záznamy s order_id = '111' a v druhé tabulce b záznamů s order_id = '111' třeba deset, tak mi to vypíše všech deset. Ale já potřebuju, aby to vzalo jen ty dva záznamy a přidělilo z tý druhý tabulky taky první dva.

Ve skutečnosti bych měl mít všechny záznamy unikátní, takže by to mělo v reálu vypadat, že v tabulce a budou jedinéčné hodnoty sloupce order_id a v druhé tabulce b zase jedinečné odpovídající hodnoty. Ale kdyby náhodou něco bylo špatně, tak určitě chci, aby se vypsal jen takový počet řádků, který je v tabulce a. Data ale potřebuju z tabulky b.
Tori
Profil
Tohle je myslím stejný případ jako Některé časteji řešené dotazy pro MySQL - FAQ » Nalezení řádků s maximální (minimální) hodnotou (ten třetí). Jen potřebujete ujasnit, podle čeho vyberete ten jeden správný řádek z tab.b - pokud budu mít v tab.a dva řádky se stejným order_id, tak se má z tab.b vybrat v obou případech stejný řádek, anebo ne?
quatzael
Profil
Tori:
No, nevím. Nevypadá to moc, že by to bylo řešení mýho problému.

Prostě potřebuju vybrat řádky z tabulky a podle podmínek za where a k nim potom dohledat data z tabulky b podle odpovídající hodnoty ze sloupce order_id, který taky ale odpovídají zároveň některým podmínkám za WHERE.
To jsem do toho příkladu zapomněl dopsat.

Celý by to mělo být asi takhle:
SELECT *
 FROM a
 JOIN b
 ON a.order_id = b.order_id
 WHERE a.user_id = 12 AND a.status = 'on' AND b.date < NOW() ORDER BY a.datum DESC LIMIT 10 , 20;

Prostě v krocích:
1. vybrat řádky z tabulky a, které odpovídají podmínkám za WHERE, týkajících se tabulky a
2. vyhledat k nim odpovídající záznamy z tabulky b, které ale také musí vyhovovat uvedeným podmínkám pro sloupce z tabulky b
Pokud nejsou všechny podmínky s plněny tak ten řádek nesmí být vůbec vypsán.

Z pořadí těchto kroků je jasné, že když po prvním kroku budou vybrány řádky, kde se dvakrát! vyskytuje nějaká hodnota ve sloupci order_id tak musí být vypsány pouze dva! řádky, nehledě na to, že ve druhé tabulce b by se nacházelo řádků s odpovídajícím order_id třeba sto a všechny by vyhovovaly daným podmínkám.

V reálu by tam mělo být pro nějakou hodnotu sloupce order_id vždy jen po jednom záznamu v každé tabulce, ale kdyby se náhodou nějakým nedopatřením v té druhé tabulce b objevilo těch záznamů víc, tak se prostě nesmí vypsat více než jich je v první tabulce a.
Tori
Profil
Pokud nejsou všechny podmínky s plněny tak ten řádek nesmí být vůbec vypsán.
--> INNER JOIN?
je to totéž jako JOIN s podmínkou

Dejme tomu, že "první odpovídající řádek z tabulky b" budu brát podle nejvyššího ID. Pak by mohlo fungovat:
SELECT a.*, b.*
FROM a
INNER JOIN (  -- pomocná tabulka
    SELECT order_id, MAX(id) id
    FROM b
    WHERE date < NOW()
    GROUP BY order_id
) ref ON a.order_id = ref.order_id
INNER JOIN b ON ref.order_id = b.order_id AND ref.id = b.id
WHERE a.user_id = 12 AND a.status = 'on' -- doplněno
ORDER BY a.datum DESC
LIMIT 10, 20
Alphard
Profil
quatzael:
Z pořadí těchto kroků je jasné, že když po prvním kroku budou vybrány řádky, kde se dvakrát! vyskytuje nějaká hodnota ve sloupci order_id tak musí být vypsány pouze dva!
:-) Bylo by hezké, kdyby počítače dělaly to, co chceme a ne, to, co jim říkáme.

Spojování tabulek je v principu tvoření kartézského součinu, což jaksi implikuje možnost zvýšení počtu řádků. Dodatečné podmínky slouží k omezení sestavované sady dat.
To co popisujete má principiálně blíže k poddotazu, který dohledává nějak specifikovaná data a skutečně nemůže připad žádné řádky.
quatzael
Profil
Tori:
Ten příklad mi nefunguje. Já tomu SQL moc do hloubky nerozumím, ale chápal bych to tak, že se tam vytvoří pomocná tabulka ref. Ale háže mi to chybu, že to řádnou tabulku ref nezná..

Navíc v tom zápise i chybí podmínky pro tabulku a:
WHERE a.user_id = 12 AND a.status = 'on' 



Alphard:
Bylo by hezké, kdyby počítače dělaly to, co chceme a ne, to, co jim říkáme.
Já se tady snažím vysvětlit, že nevím jak sestavit takový dotaz, aby mi vygeneroval to, co tady popisuju..

Vím co je kartézský součin, ale nevím jak vytvořit dotaz, který potřebuju.


Tori:
ON ref.order_id = b.order_id AND ref.id = b.id
Navíc tohle mi připadá zbytečně redundantní, mělo by přece stačit logicky jen ref.id = b.id, ne?


Alphard, Tori:
Nemělo by se to náhodou nějak řešit podmínkou WHERE EXISTS ?
quatzael
Profil
Tak na stackoverflow mi někdo poradil tohle řešení:

select . . .
from contracts_main_list cml left join
     (select cd.*, row_number() over (partition by contract_id order by <datecol> desc) as seqnum
      from contracts_detail cd
     ) cd
     on cd.contract_id = cml.contract_id and cd.seqnum = 1;

tabulka a jako contracts_main_list a tabulka b jako contracts_detail.

Problém je ten, že to nefunguje. Zřejmě MySQL nepodporuje to row_number(), nevíte někdo náhodou jak se tenhle zápis dá nějak přetransformovat na funkční zápis pro MySQL? Já jsem na to SQL opravdu totální lama..
Tori
Profil
row_number je z MSSQL.
[#4] mělo fungovat takhle: vezmu řádky z tab. a, pak si poddotazem najdu pro všechny order_id nejvyšší ID z tab. b, pak k tomu připojím tabulku b a beru z ní pro každé order_id už jen ten jeden řádek s nejvyšším ID. Nevím, proč to hlásilo neznámou tabulku. Nezkopíroval jste tam i ty komentáře?

Koukněte třeba sem, je tam jak to moje řešení, tak i odlišný join, který místo poddotazu "najdi nejvyšší pro každé order_id a podle toho připoj tabulku" používá poddotaz "připoj tabulku a pak zahoď řádky, u kterých pro stejné order_id existuje (v jiném řádku) vyšší ID": http://stackoverflow.com/questions/755918/simple-query-to-grab-max-value-for-each-id Které je rychlejší, nevím, zkuste oba s explainem.
Kajman
Profil
Tori:
Asi bude problém v tom, že quatzael nechce jen maximum, ale v případě potřeby i druhé maximum, třetí maximum... atd. dle počtu hodnot v první tabulce.

quatzael:
Mysql samo o sobě na to asi nebude vhodné. Z neplacených databází by to mělo jít v postgresql. V případě, že mysql nechcete opustit, udělejte si dva dotazy a až např. v php si hodnoty k sobě přiřaďte.
quatzael
Profil
Tori, Kajman:
Asi bude problém v tom, že quatzael nechce jen maximum, ale v případě potřeby i druhé maximum, třetí maximum
Teď nevím jak je to myšleno.. Prostě se jedná o reálnou situaci a to takovou, že je první tabulka, kde jsou sloupce user_id a contract_id. To znamená, že když má uživatel nějaké smlouvy, tak ke každé je jednou jedinečné contract_id. A detaily k těmto smlouvám jsou v druhé tabulce, každý záznam je opatřen odpovídajícím contract_id.
Každé contract_id by v reálu mělo být jedinečné, tzn. že se bude vždy nacházet po jednom v první i druhé tabulce. Jde mi jen o to, aby v případě nějakých chyb (kdyby se tam objevilo stejné contract_id vícekrát) to neházelo více řádků se stejným contract_id.


Tori:
Nezkopíroval jste tam i ty komentáře?
Zase taková lama nejsem, abych tam nechával i ty komentáře. I když jsem myslel, že tam mohou takhle být na konci řádku, ale SQL na řádky v dotazu asi nehraje..
Hází mi to chybu, že je tam neznámý sloupec v tom dotazu: ref.id
Kajman
Profil
quatzael:
tzn. že se bude vždy nacházet po jednom v první i druhé tabulce

Tak tam udělejte jedinečné klíče a máte po starostech - tím potlačíte možnost zasenení chyby s více řádky.
quatzael
Profil
Jo a místo contract_id tam má být všude spíš order_id.. Já tady asi píšu jednou jedno a potom zase druhý..


Kajman:
Tak tam udělejte jedinečné klíče a máte po starostech - tím potlačíte možnost zasenení chyby s více řádky.
To by asi šlo udělat. Co se pak ale stane, kdybych se tam pokoušel vložit stejnou hodnotu, která tam už je? Hodí to chybu a zápis se neprovede?
Kajman
Profil
quatzael:
Hodí to chybu a zápis se neprovede?

Ano.
quatzael
Profil
Kajman:
Ok a stačí potom teda normální JOIN (INNER JOIN)?
Kajman
Profil
Pokud bude vždy v druhé tabulce právě jeden záznam k záznamu z první tabulky, stačí inner join. Pokud tam může být nula řádků a chcete přesto data z první tabulky vypsat, použitje outer join.

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: