* Клауза HAVING
Публикувано на 17 март 2009 в раздел Бази от Данни.
Понякога се налага да филтрираме данните след като вече сме направили дадена калкулация. Фразата HAVING се използва за прилагане на условия върху групи (обикновено оформени чрез GROUP BY).
Ще демонстрираме това с пример. Вече знаем как можем да изкараме списък на клиентите и средните суми на техните сметки:
SELECT customers.name, AVG(accounts.amount) FROM customers JOIN accounts ON customers.id = accounts.customer_id GROUP BY customers.name;
Нека обаче да направим следното - искаме да изкараме списъка само на клиентите, чийто сметки имат средна сума по-голяма от 500. Това не би могло да стане в клауза WHERE, защото при нея все още не е пресметнато AVG(accounts.amount). Затова можем да използваме допълнително филтриране чрез клауза HAVING:
SELECT customers.name, AVG(accounts.amount) FROM customers JOIN accounts ON customers.id = accounts.customer_id GROUP BY customers.name HAVING AVG(accounts.amount) > 500;
Ето как бихме могли да използваме WHERE и HAVING в комбинация - следната заявка изкарва списък на клиентите, чийто сметки имат средна сума на акаунта по-голяма от 500 и името им започва с "M":
SELECT customers.name, AVG(accounts.amount) FROM customers JOIN accounts ON customers.id = accounts.customer_id WHERE customers.name LIKE 'M%' GROUP BY customers.name HAVING AVG(accounts.amount) > 500;
Избягвайте да използвате HAVING, когато можете. В повечето прехвърляне на клаузата WHERE в HAVING ще даде идентичен резултат, но това ще е с цената на по-бавна заявка, защото много повече информация ще бъде обработвана (няма пропускане на редове още преди изпълнение на агрегатната функция). Също така индексите не работят с клаузи HAVING.
В общи линии винаги прилагайте теоремата: "ако в условието HAVING не участват агрегатни функции, то може да бъде пренесено в WHERE".
Възможно е да използвате вложен SELECT оператор в клауза HAVING. Опитайте се да изкарате списък на клиентите, чийто сметки имат средна сума на акаунта си по-голяма от средната.
Например така:
Как може да се напише последния SELECT?
Ето още един вариант:
SELECT customers.name, AVG(accounts.amount) AS avrg
FROM customers JOIN accounts
ON customers.id = accounts.customer_id
GROUP BY customer.id
HAVING avrg > (SELECT AVG(amount) FROM accounts);
Решение на последната заявка (въпреки забележката, която ми направихте, да не използвам в HAVING нещо, което преди това не съм SELECT-нал):
SELECT name
FROM customers
WHERE id IN
(SELECT customer_id
FROM accounts
GROUP BY customer_id
HAVING AVG(amount)>(SELECT AVG(amount) FROM accounts)
);