Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Сортировка данных с использованием HAVING в SQL
HAVING — это клауза SQL, которая фильтрует результаты агрегирующих функций (GROUP BY). Часто её комбинируют с ORDER BY для сортировки отфильтрованных данных. Давайте рассмотрим различные варианты.
1. Базовая структура: GROUP BY -> HAVING -> ORDER BY
-- Базовая синтаксис
SELECT column1, COUNT(*) as count
FROM table_name
GROUP BY column1 -- Группируем
HAVING COUNT(*) > 5 -- Фильтруем группы
ORDER BY count DESC; -- Сортируем результаты
Порядок выполнения:
- FROM — получение данных
- WHERE — фильтрация индивидуальных строк
- GROUP BY — группировка
- HAVING — фильтрация групп
- SELECT — выбор столбцов
- ORDER BY — сортировка результатов
2. Сортировка по агрегирующим функциям с HAVING
-- Продажи по категориям, только популярные категории, отсортированы по выручке
SELECT
category,
SUM(amount) as total_sales,
COUNT(*) as orders_count,
AVG(amount) as avg_sale
FROM orders
GROUP BY category
HAVING SUM(amount) > 10000 -- Только категории с выручкой > 10000
ORDER BY total_sales DESC; -- Сортируем по выручке (убывание)
-- Результат:
-- category | total_sales | orders_count | avg_sale
-- Electronics | 50000 | 100 | 500
-- Home | 25000 | 200 | 125
-- Books | 12000 | 300 | 40
3. Сортировка по нескольким критериям с HAVING
-- Сотрудники по отделам: только отделы с средней зарплатой > 50000, сортируем по количеству, потом по зарплате
SELECT
department,
COUNT(*) as employee_count,
AVG(salary) as avg_salary,
SUM(salary) as total_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > 50000
ORDER BY employee_count DESC, avg_salary DESC;
-- Результат:
-- department | employee_count | avg_salary | total_salary
-- Engineering | 50 | 85000 | 4250000
-- Management | 20 | 75000 | 1500000
-- Sales | 30 | 65000 | 1950000
4. Сортировка по вычисляемому выражению с HAVING
-- Клиенты, потративие > 5000, отсортированы по остатку бюджета
SELECT
customer_id,
customer_name,
SUM(amount) as spent,
10000 - SUM(amount) as remaining_budget
FROM orders
WHERE customer_id IS NOT NULL
GROUP BY customer_id, customer_name
HAVING SUM(amount) > 5000
ORDER BY remaining_budget ASC; -- Сортируем по вычисляемому полю
-- Результат:
-- customer_id | customer_name | spent | remaining_budget
-- 5 | John | 9500 | 500
-- 3 | Alice | 8000 | 2000
-- 7 | Bob | 6000 | 4000
5. Комплексные условия HAVING с множественной сортировкой
-- Продукты: количество продано > 100 И выручка > 50000, сортируем по выручке и количеству
SELECT
product_id,
product_name,
COUNT(*) as quantity_sold,
SUM(price) as revenue,
AVG(price) as avg_price
FROM sales
GROUP BY product_id, product_name
HAVING COUNT(*) > 100 AND SUM(price) > 50000 -- Сложное условие
ORDER BY revenue DESC, quantity_sold DESC; -- Множественная сортировка
-- Результат:
-- product_id | product_name | quantity_sold | revenue | avg_price
-- 1 | Laptop | 500 | 500000 | 1000
-- 2 | Mouse | 2000 | 80000 | 40
-- 5 | Keyboard | 800 | 60000 | 75
6. HAVING с условиями на разные агрегирующие функции
-- Месячные продажи: средняя > 1000 И минимум > 500, сортируем по максимуму
SELECT
EXTRACT(YEAR_MONTH FROM date) as month,
COUNT(*) as total_orders,
SUM(amount) as total_amount,
AVG(amount) as avg_amount,
MIN(amount) as min_amount,
MAX(amount) as max_amount
FROM orders
GROUP BY EXTRACT(YEAR_MONTH FROM date)
HAVING AVG(amount) > 1000 AND MIN(amount) > 500
ORDER BY MAX(amount) DESC;
-- Результат:
-- month | total_orders | total_amount | avg_amount | min_amount | max_amount
-- 202603 | 150 | 200000 | 1333.33 | 600 | 5000
-- 202602 | 120 | 150000 | 1250 | 750 | 4500
7. HAVING с BETWEEN и сортировкой
-- Возрастные группы клиентов: заказы в диапазоне 50-200, сортируем по среднему возрасту
SELECT
CASE
WHEN age < 20 THEN 'Under 20'
WHEN age < 30 THEN '20-29'
WHEN age < 40 THEN '30-39'
ELSE '40+'
END as age_group,
COUNT(*) as customer_count,
AVG(age) as avg_age,
SUM(total_spent) as group_revenue
FROM customers
GROUP BY age_group
HAVING COUNT(*) BETWEEN 50 AND 200 -- HAVING с BETWEEN
ORDER BY avg_age ASC; -- Сортируем по среднему возрасту
-- Результат:
-- age_group | customer_count | avg_age | group_revenue
-- 20-29 | 180 | 25.5 | 450000
-- 30-39 | 165 | 35.2 | 520000
-- Under 20 | 75 | 18.3 | 150000
8. HAVING с IN и сортировкой
-- Страны: всего заказов в {50, 100, 150}, отсортировать по выручке
SELECT
country,
COUNT(*) as order_count,
SUM(amount) as revenue,
AVG(amount) as avg_order
FROM orders
GROUP BY country
HAVING COUNT(*) IN (50, 100, 150) -- HAVING с IN
ORDER BY revenue DESC;
-- Результат:
-- country | order_count | revenue | avg_order
-- USA | 100 | 150000 | 1500
-- UK | 50 | 80000 | 1600
-- Canada | 150 | 120000 | 800
9. Пример на Spring Data JPA (аналог SQL)
// Эквивалент SQL запроса с GROUP BY, HAVING, ORDER BY
@Repository
public interface OrderRepository extends JpaRepository<Order, UUID> {
// Запрос с GROUP BY, HAVING и ORDER BY
@Query("""
SELECT new map(
o.category as category,
COUNT(o) as orderCount,
SUM(o.amount) as totalAmount,
AVG(o.amount) as avgAmount
)
FROM Order o
GROUP BY o.category
HAVING SUM(o.amount) > :minRevenue
ORDER BY SUM(o.amount) DESC, COUNT(o) DESC
""")
List<Map<String, Object>> findCategoriesWithRevenue(@Param("minRevenue") BigDecimal minRevenue);
}
// Использование
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
public List<Map<String, Object>> getPopularCategories() {
// SQL: SELECT category, COUNT(*), SUM(amount), AVG(amount)
// FROM orders
// GROUP BY category
// HAVING SUM(amount) > 10000
// ORDER BY SUM(amount) DESC
return orderRepository.findCategoriesWithRevenue(BigDecimal.valueOf(10000));
}
}
10. Сортировка с LIMIT и HAVING
-- Топ-5 продавцов по выручке (минимум 20 продаж), отсортированы по выручке
SELECT
seller_id,
seller_name,
COUNT(*) as sale_count,
SUM(amount) as revenue
FROM sales
GROUP BY seller_id, seller_name
HAVING COUNT(*) >= 20
ORDER BY revenue DESC
LIMIT 5; -- Только топ-5
-- Результат:
-- seller_id | seller_name | sale_count | revenue
-- 5 | Alice | 150 | 500000
-- 3 | Bob | 120 | 450000
-- 7 | Charlie | 100 | 380000
-- 2 | Diana | 95 | 350000
-- 8 | Eve | 85 | 300000
Ключевые отличия: WHERE vs HAVING
| Критерий | WHERE | HAVING |
|---|---|---|
| Что фильтрует | Индивидуальные строки | Группы (результаты GROUP BY) |
| Используется с | Любых данных | Только с GROUP BY |
| Агрегирующие функции | Нельзя в WHERE | Можно в HAVING |
| Выполняется | До GROUP BY | После GROUP BY |
| Пример | WHERE salary > 50000 | HAVING AVG(salary) > 50000 |
Пример: WHERE + HAVING + ORDER BY
-- Все вместе: показать отделы, где сотрудники с зарплатой > 40000 в количестве > 5, отсортировать по средней зарплате
SELECT
department,
COUNT(*) as employee_count,
AVG(salary) as avg_salary,
SUM(salary) as total_salary
FROM employees
WHERE salary > 40000 -- Фильтруем до группировки (индивидуальные строки)
GROUP BY department
HAVING COUNT(*) > 5 -- Фильтруем после группировки (группы)
ORDER BY avg_salary DESC; -- Сортируем результаты
-- Результат содержит только отделы с >5 сотрудниками, у которых зарплата > 40000
Рекомендации
- Используй WHERE для индивидуальных условий — это быстрее (меньше данных в GROUP BY)
- Используй HAVING для агрегирующих условий — это единственный способ фильтровать группы
- ORDER BY идёт в конце — всегда последний клауза
- Комбинируй множественные условия в ORDER BY для лучшей читаемости
- Тестируй производительность — сложные HAVING условия могут быть дорогими
Мастерство работы с GROUP BY, HAVING и ORDER BY критично для написания эффективных SQL запросов, особенно для аналитики и отчётов.