sql >> Databáze >  >> RDS >> Mysql

Jak správně GROUP BY v MySQL?

První věc, kterou je třeba objasnit, je, že SQL není MySQL.

Ve standardním SQL není povoleno seskupování podle podmnožiny neagregovaných polí. Důvod je velmi jednoduchý. Předpokládejme, že spouštím tento dotaz:

SELECT color, owner_name, COUNT(*) FROM cars
GROUP BY color

Ten dotaz by nedával smysl. I pokusit se to vysvětlit by bylo nemožné. Samozřejmostí je výběr barev a počítání množství aut na barvu. Přidává však také owner_name pole a pro danou barvu může být mnoho vlastníků, jako je tomu v případě White barva. Pokud tedy může být mnoho owner_name hodnoty pro jednu color což je shodou okolností jediné pole v GROUP BY klauzule... pak které owner_name bude vrácen?

Pokud je potřeba vrátit owner_name pak by se mělo přidat nějaké kritérium, aby bylo možné vybrat pouze jedno z nich, např. první podle abecedy, což by v tomto případě bylo John . Tato kritéria by vedla k přidání agregační funkce MIN(owner_name) a pak bude dotaz opět dávat smysl, protože se bude seskupovat alespoň podle všech neagregovaných polí v příkazu select.

Jak vidíte, existuje jasný a praktický důvod, proč je standardní SQL v seskupování neflexibilní. Pokud by tomu tak nebylo, mohli byste čelit nepříjemným situacím, ve kterých bude hodnota sloupce nepředvídatelná, a to není hezké slovo, zvláště pokud spuštěný dotaz zobrazuje transakce vašeho bankovního účtu.

Proč by tedy MySQL umožňovalo dotazy, které nemusí dávat smysl? A co je ještě horší, chyba v dotazu výše by mohla být jen syntakticky odhalena! Krátká odpověď zní:výkon. Dlouhá odpověď je, že existují určité situace, ve kterých na základě datových vztahů získání nepředvídatelné hodnoty ze skupiny povede k předvídatelné hodnotě.

Pokud jste na to ještě nepřišli, jediný způsob, jak můžete předpovědět hodnotu, kterou získáte odebráním nepředvídatelného prvku ze skupiny, bude, pokud budou všechny prvky ve skupině stejné. Jasným příkladem této situace je ukázkový dotaz ve vaší stejné otázce. Podívejte se, jak owner_id a owner_name souvisí v tabulce. Je jasné, že zadané jakékoli owner_id , např. 2 , můžete mít pouze jedno odlišné owner_name . I když máte mnoho řádků, výběrem libovolného získáte Mike jako výsledek. Ve formálním databázovém žargonu to lze vysvětlit jako owner_id funkčně určuje owner_name .

Podívejme se blíže na tento plně funkční dotaz MySQL:

SELECT owner_id, owner_name, COUNT(*) total FROM cars
GROUP BY owner_id

Zadané jakékoli owner_id to by vrátilo stejné owner_name , takže jej přidejte do GROUP BY klauzule nebude mít za následek více vrácených řádků. Dokonce i přidání agregované funkce MAX(owner_name) nebude mít za následek méně vrácených řádků. Výsledná data budou naprosto stejná. V obou případech by se dotaz okamžitě změnil na právní standardní SQL dotaz, protože by alespoň všechna neagregovaná pole byla seskupena podle. Existují tedy 3 přístupy k dosažení stejných výsledků.

Jak jsem však již zmínil, toto nestandardní seskupení má výkonnostní výhodu. Můžete se podívat na tento tak podceňovaný odkaz ve kterém je to vysvětleno podrobněji, ale budu citovat nejdůležitější část:

Jedna věc, která stojí za zmínku, je, že výsledky nemusí být nutně špatné ale spíše neurčité . Jinými slovy, získání očekávaných výsledků neznamená, že jste napsali správný dotaz. Napsání správného dotazu vám vždy poskytne očekávané výsledky.

Jak vidíte, možná by stálo za to použít toto rozšíření MySQL na GROUP BY doložka. Každopádně, pokud to ještě není 100% jasné, pak existuje základní pravidlo, které zajistí, že vaše seskupení bude vždy správné:Vždy seskupujte alespoň podle všech neagregovaných polí v klauzuli select . V určitých situacích můžete plýtvat několika cykly CPU, ale je to lepší než vracet neurčité Výsledek. Pokud se stále bojíte nesprávného seskupování, změňte ONLY_FULL_GROUP_BY Režim SQL by mohl být poslední možností :)

Ať je vaše seskupení správné a výkonné... nebo alespoň správné.




  1. Vnitřní spojení SQLite

  2. Kde je soubor jar MySQL JDBC v Ubuntu?

  3. MariaDB představí TO_CHAR()

  4. Jak zavřít připojení sqlalchemy v MySQL