← Назад к вопросам

Какое условие есть в HAVING, которое нельзя использовать в WHERE?

1.2 Junior🔥 141 комментариев
#Базы данных (SQL)

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

HAVING vs WHERE — различия в SQL

Важное различие между HAVING и WHERE заключается в том, когда они применяются в процессе обработки запроса и какие условия они могут проверять. Это одна из классических ошибок в SQL.

Основное различие

WHERE применяется перед группировкой данных (GROUP BY) HAVING применяется после группировки и работает с агрегатными функциями

Агрегатные функции в HAVING

В HAVING можно использовать агрегатные функции (COUNT, SUM, AVG, MIN, MAX), которые нельзя использовать в WHERE:

-- Найти пользователей, у которых более 5 заказов
SELECT user_id, COUNT(*) as order_count
FROM orders
GROUP BY user_id
HAVING COUNT(*) > 5;  -- ✅ Правильно — COUNT в HAVING

-- ОШИБКА: это не сработает
SELECT user_id
FROM orders
WHERE COUNT(*) > 5;  -- ❌ Ошибка — COUNT нельзя в WHERE

Практические примеры

Пример 1: Найти категории товаров с суммой продаж более 1000

SELECT 
    category,
    SUM(price * quantity) as total_sales
FROM products
GROUP BY category
HAVING SUM(price * quantity) > 1000;  -- ✅ HAVING с агрегатом

Это нельзя сделать в WHERE:

-- ❌ Неправильно — WHERE не знает о GROUP BY
SELECT 
    category,
    SUM(price * quantity) as total_sales
FROM products
WHERE SUM(price * quantity) > 1000  -- ❌ ОШИБКА!
GROUP BY category;

Пример 2: Найти пользователей с средним рейтингом больше 4.5

SELECT 
    user_id,
    AVG(rating) as avg_rating
FROM reviews
GROUP BY user_id
HAVING AVG(rating) > 4.5;  -- ✅ HAVING с AVG

Порядок обработки запроса

SELECT category, COUNT(*) as count, AVG(price) as avg_price
FROM products
WHERE price > 100        -- 1️⃣ Первый — фильтруем строки ПЕРЕД группировкой
GROUP BY category        -- 2️⃣ Второй — группируем
HAVING COUNT(*) > 5      -- 3️⃣ Третий — фильтруем ПОСЛЕ группировки
ORDER BY avg_price DESC; -- 4️⃣ Четвертый — сортируем

Порядок выполнения:

  1. WHERE — фильтрует записи (price > 100)
  2. GROUP BY — группирует результаты по категориям
  3. HAVING — фильтрует группы (COUNT(*) > 5)
  4. ORDER BY — сортирует результат

Пример на Python с SQLAlchemy

from sqlalchemy import func, select
from sqlalchemy.orm import Session
from models import Product

def get_categories_with_high_sales(session: Session):
    """Найти категории с суммой продаж более 1000."""
    
    query = select(
        Product.category,
        func.sum(Product.price * Product.quantity).label('total_sales')
    ).group_by(
        Product.category
    ).having(
        func.sum(Product.price * Product.quantity) > 1000  # HAVING с агрегатом
    )
    
    return session.execute(query).fetchall()

def get_products_with_discount(session: Session):
    """Найти товары со скидкой."""
    
    query = select(Product).where(
        Product.price < 100  # WHERE — фильтр перед группировкой
    )
    
    return session.execute(query).fetchall()

# Комбинированный пример
def complex_query(session: Session):
    """Сложный запрос с WHERE и HAVING."""
    
    query = select(
        Product.category,
        func.count(Product.id).label('product_count'),
        func.avg(Product.price).label('avg_price')
    ).where(
        Product.is_active == True  # WHERE — перед группировкой
    ).group_by(
        Product.category
    ).having(
        func.count(Product.id) > 3,  # HAVING — после группировки
        func.avg(Product.price) > 50
    ).order_by(
        func.avg(Product.price).desc()
    )
    
    return session.execute(query).fetchall()

Полный пример с Django ORM

from django.db.models import Count, Sum, Avg, Q, F
from models import Order, User

# Найти пользователей с более чем 10 заказами и суммой > 5000
users = User.objects.annotate(
    order_count=Count('orders'),
    total_amount=Sum('orders__amount')
).filter(
    order_count__gt=10,  # HAVING эквивалент в Django
    total_amount__gt=5000
)

# Это эквивалентно:
SELECT u.id, COUNT(o.id) as order_count, SUM(o.amount) as total_amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id
HAVING COUNT(o.id) > 10 AND SUM(o.amount) > 5000;

Типичные ошибки

Ошибка 1: Использование агрегата в WHERE

-- ❌ ОШИБКА
SELECT department, AVG(salary)
FROM employees
WHERE AVG(salary) > 50000  -- ❌ Нельзя AGG функции в WHERE
GROUP BY department;

-- ✅ Правильно
SELECT department, AVG(salary)
FROM employees
GROUP BY department
HAVING AVG(salary) > 50000;

Ошибка 2: Забыли GROUP BY

-- ❌ ОШИБКА
SELECT department, COUNT(*)
FROM employees;
-- без GROUP BY это ошибка (в strict mode)

-- ✅ Правильно
SELECT department, COUNT(*)
FROM employees
GROUP BY department;

Ошибка 3: Использование неагрегированного поля в SELECT с GROUP BY

-- ❌ ОШИБКА в PostgreSQL/Oracle
SELECT name, department, COUNT(*)  -- name не в GROUP BY!
FROM employees
GROUP BY department;

-- ✅ Правильно
SELECT department, COUNT(*)
FROM employees
GROUP BY department;

-- Или используй MAX/MIN для name
SELECT MAX(name), department, COUNT(*)
FROM employees
GROUP BY department;

Сравнительная таблица

ХарактеристикаWHEREHAVING
Когда применяетсяПеред GROUP BYПосле GROUP BY
Работает сОтдельные строкиГруппы строк
Агрегатные функции❌ Нельзя✅ Можно
ПроизводительностьБыстрееМедленнее
Примеры условийprice > 100COUNT(*) > 5
is_active = trueSUM(amount) > 1000
name LIKE '%John%'AVG(rating) > 4.5

Оптимизация

-- Правильный порядок для производительности:

-- 1️⃣ WHERE сначала — фильтруем как можно раньше
SELECT category, COUNT(*)
FROM products
WHERE price > 100 AND is_active = true  -- Фильтруем ДО группировки
GROUP BY category
HAVING COUNT(*) > 5;  -- Фильтруем ПОСЛЕ группировки

-- Это быстрее, чем:
SELECT category, COUNT(*)
FROM products
GROUP BY category
HAVING COUNT(*) > 5 AND price > 100;  -- Неэффективно!

Вывод: HAVING используется для фильтрации результатов группировки с агрегатными функциями, WHERE — для фильтрации исходных строк. Это ключевое различие для написания правильных и эффективных SQL запросов.

Какое условие есть в HAVING, которое нельзя использовать в WHERE? | PrepBro