sqlcmd.net validated sql reference
intermediate date-time MySQL MariaDB SQL Server PostgreSQL SQLite

Group Rows By Month

Bucket dates into month starts, then aggregate counts or totals per month.

Docker-validated Not currently validation-green

Count orders and total revenue per month

March has two orders worth 100 total. April has three orders worth 150 total. The query first maps each order_date to the first day of its month, then aggregates on that derived month value.

MySQL MariaDB
Engine-specific syntax
Setup
CREATE TABLE orders (id INT, order_date DATE, amount INT);

INSERT INTO
  orders (id, order_date, amount)
VALUES
  (1, '2024-03-03', 40),
  (2, '2024-03-18', 60),
  (3, '2024-04-02', 75),
  (4, '2024-04-25', 25),
  (5, '2024-04-30', 50);
SQL
SELECT
  DATE_FORMAT (order_date, '%Y-%m-01') AS month_start,
  COUNT(*) AS total_orders,
  SUM(amount) AS total_amount
FROM
  orders
GROUP BY
  DATE_FORMAT (order_date, '%Y-%m-01')
ORDER BY
  month_start;
month_starttotal_orderstotal_amount
2024-03-012100
2024-04-013150
SQL Server
Engine-specific syntax
Setup
CREATE TABLE orders (id INT, order_date DATE, amount INT);

INSERT INTO
  orders (id, order_date, amount)
VALUES
  (1, '2024-03-03', 40),
  (2, '2024-03-18', 60),
  (3, '2024-04-02', 75),
  (4, '2024-04-25', 25),
  (5, '2024-04-30', 50);
SQL
SELECT
  CONVERT(
    VARCHAR(10),
    DATEADD (MONTH, DATEDIFF (MONTH, 0, order_date), 0),
    23
  ) AS month_start,
  COUNT(*) AS total_orders,
  SUM(amount) AS total_amount
FROM
  orders
GROUP BY
  DATEADD (MONTH, DATEDIFF (MONTH, 0, order_date), 0)
ORDER BY
  month_start;
month_starttotal_orderstotal_amount
2024-03-012100
2024-04-013150
PostgreSQL
Engine-specific syntax
Setup
CREATE TABLE orders (id INT, order_date DATE, amount INT);

INSERT INTO
  orders (id, order_date, amount)
VALUES
  (1, '2024-03-03', 40),
  (2, '2024-03-18', 60),
  (3, '2024-04-02', 75),
  (4, '2024-04-25', 25),
  (5, '2024-04-30', 50);
SQL
SELECT TO_CHAR(DATE_TRUNC('month', order_date::TIMESTAMP), 'YYYY-MM-DD') AS month_start, COUNT(*) AS total_orders, SUM(amount) AS total_amount FROM orders GROUP BY DATE_TRUNC('month', order_date::TIMESTAMP) ORDER BY month_start;
month_starttotal_orderstotal_amount
2024-03-012100
2024-04-013150
SQLite
Engine-specific syntax
Setup
CREATE TABLE orders (id INT, order_date DATE, amount INT);

INSERT INTO
  orders (id, order_date, amount)
VALUES
  (1, '2024-03-03', 40),
  (2, '2024-03-18', 60),
  (3, '2024-04-02', 75),
  (4, '2024-04-25', 25),
  (5, '2024-04-30', 50);
SQL
SELECT
  strftime ('%Y-%m-01', order_date) AS month_start,
  COUNT(*) AS total_orders,
  SUM(amount) AS total_amount
FROM
  orders
GROUP BY
  strftime ('%Y-%m-01', order_date)
ORDER BY
  month_start;
month_starttotal_orderstotal_amount
2024-03-012100
2024-04-013150

The month-bucketing function differs by engine, but the grouped monthly totals are identical.

Where this command helps.

  • building monthly order or revenue summaries
  • grouping event data into reporting periods before charting

What the command is doing.

Monthly reporting usually needs two steps: convert each date to its month bucket, then aggregate on that bucket. The exact truncation function differs by engine, but the pattern is the same. Once each row maps to the first day of its month, GROUP BY can count rows, sum amounts, or compute other aggregates per month.