Онлайн час в Google Meet
Лого на 91. НЕГ „Проф. Константин Гълъбов“

12. клас · Избираем модул · Урок 4

Агрегатни функции и сложни групирания. HAVING

COUNT, SUM, AVG, MIN, MAX и ROUND; GROUP BY по една и по няколко колони; HAVING срещу WHERE; групиране върху съединение и подреждане на обобщените резултати.

От редове към обобщения

Почти всяка справка, която ще ви поискат — в проекта, в практикума в седмица 9, от бъдещ работодател — е обобщение: колко, средно, най-много, по класове, по месеци. В Модул 3 (урок „ORDER BY и GROUP BY“) научихте агрегатните функции и групирането по една колона. Днес ги събираме с всичко от последните два часа: групиране върху JOIN, по няколко колони, с филтър след групирането (HAVING) и с подреждане на резултата.

Агрегатните функции — с тънкостите

ФункцияПресмятаКакво прави с NULL
COUNT(*)брой редове в групатаброи и тях
COUNT(колона)брой не-NULL стойностипропуска ги
COUNT(DISTINCT колона)брой различни стойностипропуска ги
SUM(колона)сумапропуска ги; върху 0 реда дава NULL
AVG(колона)средно аритметичнопропуска ги — средното е от попълнените
MIN / MAX(колона)най-малка / най-голямапропуска ги
ROUND(израз, 2)закръгля до 2 знакаNULL остава NULL
  • AVG пропуска NULL: ако в клас с 3 ученици един е без успех, средното е от двамата, а не от тримата. Ако искате „от всички“, трябва SUM(grade) / COUNT(*).
  • AVG дава дълга дроб (5.0055…). За справка я закръгляте с ROUND(AVG(grade), 2) — и целият израз получава име с AS.
  • Агрегат без GROUP BY = една група от всички редове = един ред резултат.

GROUP BY по една и по няколко колони

GROUP BY колона1 [, колона2, …]

Редовете с еднаква комбинация от стойности в изброените колони образуват една група. Резултатът има по един ред на група; в него може да участват само групиращите колони и агрегати.

-- Една колона: една група на клас
SELECT class_id, COUNT(*) AS broy, ROUND(AVG(grade), 2) AS sreden
FROM students
GROUP BY class_id;

-- Две колони: една група на двойка (ученик, предмет)
SELECT student_id, subject_id, COUNT(*) AS broy, MAX(mark) AS nay_visoka
FROM marks
GROUP BY student_id, subject_id;

Правилото за колоните в SELECT

Всичко в SELECT, което не е агрегат, трябва да е в GROUP BY. Иначе за групата има много стойности, а място — за една. MySQL (в стандартния режим ONLY_FULL_GROUP_BY) отказва такава заявка с грешка. SQLite я изпълнява и мълчаливо взема стойността от произволен ред на групата — заявката „работи“, но резултатът е ненадежден.

HAVING срещу WHERE

WHEREHAVING
Кога действапреди групиранетослед групирането
Върху каквоотделни редовецели групи
Може ли агрегатне — AVG(grade) в WHERE е грешкада — точно за това е
Примерсамо оценките от 2026 г.само предметите със среден успех над 5
-- Двете заедно: първо отсяваме редове, после - групи
SELECT subject_id, ROUND(AVG(mark), 2) AS sreden, COUNT(*) AS broy
FROM marks
WHERE date >= '2026-02-12'       -- редове: само по-новите оценки
GROUP BY subject_id
HAVING COUNT(*) >= 3             -- групи: само предмети с поне 3 такива
ORDER BY sreden DESC;

Групиране върху съединение

Истинските справки групират по имена, а те са в други таблици. Затова първо съединявате, после групирате. Тук се връщат уроците от миналия час: с INNER JOIN групите без данни изчезват; с LEFT JOIN и COUNT(колона) те излизат с нула.

-- Брой оценки на всеки ученик - и на тези с нула
SELECT s.name, COUNT(m.id) AS broy
FROM students s
LEFT JOIN marks m ON m.student_id = s.id
GROUP BY s.id, s.name
ORDER BY broy DESC, s.name;
  • Групирайте по ключа на „едно“-таблицата (s.id), не по името — двама ученици с еднакво име биха се слели.
  • При три и повече таблици (третия пример) групите са по комбинация от ключове: GROUP BY c.id, sub.id дава „клас × предмет“.
  • Подреждайте обобщения резултат по агрегата (ORDER BY sreden DESC) и добавяйте второ поле за равните стойности, за да е редът предвидим.

Какво трябва да запомниш

  • COUNT(*) брои редове, COUNT(колона) — не-NULL стойности; SUM, AVG, MIN, MAX пропускат NULL.
  • GROUP BY по няколко колони = една група на всяка комбинация; в SELECT — само групиращи колони и агрегати.
  • WHERE отсява редове преди групирането; HAVING — групи след него. Агрегат само в HAVING.
  • Ред на конвейера: FROM/JOIN → WHERE → GROUP BY → HAVING → SELECT → ORDER BY.
  • Групиране върху LEFT JOIN + COUNT(колона) показва и групите с нула.
  • ROUND(AVG(...), 2) AS име — закръглете и наименувайте всеки агрегат.