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

Какие знаешь сортировки с HAVING?

1.3 Junior🔥 171 комментариев
#Базы данных и SQL

Комментарии (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;   -- Сортируем результаты

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

  1. FROM — получение данных
  2. WHERE — фильтрация индивидуальных строк
  3. GROUP BY — группировка
  4. HAVING — фильтрация групп
  5. SELECT — выбор столбцов
  6. 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

КритерийWHEREHAVING
Что фильтруетИндивидуальные строкиГруппы (результаты GROUP BY)
Используется сЛюбых данныхТолько с GROUP BY
Агрегирующие функцииНельзя в WHEREМожно в HAVING
ВыполняетсяДо GROUP BYПосле GROUP BY
ПримерWHERE salary > 50000HAVING 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

Рекомендации

  1. Используй WHERE для индивидуальных условий — это быстрее (меньше данных в GROUP BY)
  2. Используй HAVING для агрегирующих условий — это единственный способ фильтровать группы
  3. ORDER BY идёт в конце — всегда последний клауза
  4. Комбинируй множественные условия в ORDER BY для лучшей читаемости
  5. Тестируй производительность — сложные HAVING условия могут быть дорогими

Мастерство работы с GROUP BY, HAVING и ORDER BY критично для написания эффективных SQL запросов, особенно для аналитики и отчётов.

Какие знаешь сортировки с HAVING? | PrepBro