C, PHP, VB, .NET

Дневникът на Филип Петров


* Клауза 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. Опитайте се да изкарате списък на клиентите, чийто сметки имат средна сума на акаунта си по-голяма от средната.

 



4 коментара


  1. Ето още един вариант:

    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);

  2. Решение на последната заявка (въпреки забележката, която ми направихте, да не използвам в 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)
    );

Добави коментар

Адресът на електронната поща няма да се публикува


*