iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

GROUP BY

GROUP BY collapses rows that share a key into one summary row. Combine with aggregates (COUNT, SUM, AVG, GROUP_CONCAT) and filter the groups with HAVING.

Aggregates, HAVING, JSON, rollup

EXAMPLE
-- 1) Count per group
SELECT  country, COUNT(*) AS users
FROM    users
GROUP   BY country
ORDER   BY users DESC;

-- 2) Multiple aggregates
SELECT  category,
        COUNT(*)         AS n,
        AVG(price)       AS avg_price,
        MIN(price)       AS min_price,
        MAX(price)       AS max_price,
        SUM(stock)       AS total_stock
FROM    products
GROUP   BY category;

-- 3) HAVING — filters GROUPS (not rows)
SELECT  user_id, COUNT(*) AS posts
FROM    posts
GROUP   BY user_id
HAVING  posts >= 10                  -- MySQL allows alias here
ORDER   BY posts DESC;

-- 4) GROUP_CONCAT — comma-separated list per group
SELECT  user_id,
        GROUP_CONCAT(
            DISTINCT tag
            ORDER BY tag
            SEPARATOR ', '
        ) AS tags
FROM    post_tags pt
JOIN    tags t ON t.id = pt.tag_id
GROUP   BY user_id;

-- 5) JSON aggregates — return related rows as a JSON array
SELECT  u.id, u.email,
        JSON_ARRAYAGG(JSON_OBJECT('id', p.id, 'title', p.title)) AS posts
FROM    users u
LEFT    JOIN posts p ON p.user_id = u.id
GROUP   BY u.id, u.email;

-- 6) WITH ROLLUP — subtotal + grand total
SELECT  country, plan, COUNT(*) AS n
FROM    users
GROUP   BY country, plan WITH ROLLUP;
-- Returns per-(country, plan), per-country (plan = NULL), grand (country = NULL, plan = NULL)

-- 7) GROUPING() helps identify the rollup level
SELECT  IFNULL(country, 'All countries') AS country,
        IFNULL(plan,    'All plans')     AS plan,
        COUNT(*) AS n,
        GROUPING(country) AS is_country_total,
        GROUPING(plan)    AS is_plan_total
FROM    users
GROUP   BY country, plan WITH ROLLUP;

-- 8) Strict mode (recommended)
SET sql_mode = CONCAT(@@sql_mode, ',ONLY_FULL_GROUP_BY');
-- now SELECT-list columns must be in GROUP BY or aggregated — same behaviour as Postgres

Why it matters

GROUP_CONCAT + JSON_ARRAYAGG let you return parent + child data in one query. Beats the N+1 of issuing one SELECT per group from the app.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
SELECT DATE(created_at) AS day, COUNT(*) AS n
FROM events
WHERE created_at >= NOW() - INTERVAL 7 DAY
GROUP BY day
ORDER BY day;
Try it Yourself »

Discussion

Loading…