Първото нещо, което трябва да стане ясно, е, че SQL не е MySQL.
В стандартния SQL не е разрешено групирането по подмножество от неагрегирани полета. Причината е много проста. Да предположим, че изпълнявам тази заявка:
SELECT color, owner_name, COUNT(*) FROM cars
GROUP BY color
Това запитване няма да има никакъв смисъл. Дори да се опитам да го обясня би било невъзможно. Със сигурност избира цветове и брои количеството коли за цвят. Въпреки това, той също така добавя owner_name
поле и може да има много собственици за даден цвят, какъвто е случаят с White
цвят. Така че, ако може да има много owner_name
стойности за един color
което се оказва единственото поле в GROUP BY
клауза... след това кое owner_name
ще бъде върнато?
Ако е необходимо да се върне owner_name
тогава трябва да се добави някакъв вид критерии, за да се избере само един от тях, например първият по азбучен ред, който в този случай би бил John
. Тези критерии биха довели до добавяне на агрегатна функция MIN(owner_name)
и след това заявката отново ще има смисъл, тъй като ще се групира поне по всички неагрегирани полета в оператора select.
Както можете да видите, има ясна и практична причина стандартният SQL да бъде негъвкав в групирането. Ако не беше, бихте могли да се сблъскате с неудобни ситуации, в които стойността за колона ще бъде непредсказуема и това не е хубава дума, особено ако изпълняваната заявка ви показва транзакциите по банковата ви сметка.
След като казахме това, тогава защо MySQL позволява заявки, които може да нямат смисъл? И още по-лошо, грешката в заявката по-горе може да бъде просто синтактично открита! Краткият отговор е:производителност. Дългият отговор е, че има определени ситуации, в които въз основа на отношенията с данни получаването на непредвидима стойност от групата ще доведе до предвидима стойност.
Ако все още не сте го разбрали, единственият начин, по който можете да предвидите стойността, която ще получите от вземането на непредвидим елемент от група, ще бъде ако всички елементи в групата са еднакви. Ясен пример за тази ситуация е в примерната заявка в същия въпрос. Вижте как owner_id
и owner_name
се отнася в таблицата. Ясно е, че даден owner_id
, напр. 2
, можете да имате само един отделен owner_name
. Дори да имате много редове, като изберете някой, ще получите Mike
като резултат. На официалния жаргон на базата данни това може да се обясни като owner_id
функционално определя owner_name
.
Нека разгледаме по-отблизо тази напълно работеща MySQL заявка:
SELECT owner_id, owner_name, COUNT(*) total FROM cars
GROUP BY owner_id
Даден всеки owner_id
това ще върне същото owner_name
, така че го добавяте към GROUP BY
клаузата няма да доведе до повече върнати редове. Дори добавяне на агрегирана функция MAX(owner_name)
няма да доведе до по-малко върнати редове. Получените данни ще бъдат абсолютно същите. И в двата случая заявката ще бъде незабавно превърната в правна стандартна SQL заявка, тъй като поне всички неагрегирани полета ще бъдат групирани по. Така че има 3 подхода за получаване на едни и същи резултати.
Въпреки това, както споменах по-рано, това нестандартно групиране има предимство в производителността. Можете да проверите тази толкова подценена връзка в който това е обяснено за повече подробности, но ще цитирам най-важната част:
Едно нещо, което си струва да се спомене е, че резултатите не са непременно грешни но по-скоро неопределено . С други думи, получаването на очакваните резултати не означава, че сте написали правилната заявка. Написването на правилната заявка винаги ще ви даде очакваните резултати.
Както можете да видите, може да си струва да приложите това разширение на MySQL към GROUP BY
клауза. Както и да е, ако това все още не е 100% ясно, тогава има практическо правило, което ще гарантира, че вашето групиране винаги ще бъде правилно:Винаги групирайте поне по всички неагрегирани полета в клаузата за избор . Може да губите няколко цикъла на процесора в определени ситуации, но това е по-добре, отколкото да връщате неопределено резултати. Ако все още се страхувате да не групирате правилно, тогава променете ONLY_FULL_GROUP_BY
SQL режимът може да е последна мярка :)
Нека вашето групиране е правилно и ефективно... или поне правилно.