Writing / Netsuite

SuiteQL GROUP BY Month, Quarter and Year

Group SuiteQL results by calendar month, quarter or year while keeping the output correctly sorted across multiple years.

For reliable chronological grouping, keep numeric year and period values in the result instead of sorting only by formatted labels.

Group by month

SELECT
  EXTRACT(YEAR FROM transaction.trandate) AS report_year,
  EXTRACT(MONTH FROM transaction.trandate) AS report_month,
  COUNT(*) AS transaction_count,
  SUM(transaction.foreigntotal) AS total_amount
FROM transaction
WHERE transaction.trandate >= TO_DATE(?, 'YYYY-MM-DD')
  AND transaction.trandate <  TO_DATE(?, 'YYYY-MM-DD')
GROUP BY
  EXTRACT(YEAR FROM transaction.trandate),
  EXTRACT(MONTH FROM transaction.trandate)
ORDER BY
  report_year,
  report_month

Grouping by month without the year combines January 2025 with January 2026.

Add a readable month label

The label can be selected alongside the numeric sort fields:

SELECT
  EXTRACT(YEAR FROM transaction.trandate) AS report_year,
  EXTRACT(MONTH FROM transaction.trandate) AS report_month,
  TO_CHAR(transaction.trandate, 'YYYY-MM') AS month_label,
  COUNT(*) AS transaction_count
FROM transaction
GROUP BY
  EXTRACT(YEAR FROM transaction.trandate),
  EXTRACT(MONTH FROM transaction.trandate),
  TO_CHAR(transaction.trandate, 'YYYY-MM')
ORDER BY report_year, report_month

Every selected non-aggregate expression must also appear in GROUP BY.

Group by quarter

TO_CHAR(date, 'Q') returns the calendar quarter:

SELECT
  EXTRACT(YEAR FROM transaction.trandate) AS report_year,
  TO_CHAR(transaction.trandate, 'Q') AS report_quarter,
  COUNT(*) AS transaction_count
FROM transaction
GROUP BY
  EXTRACT(YEAR FROM transaction.trandate),
  TO_CHAR(transaction.trandate, 'Q')
ORDER BY report_year, report_quarter

This is a calendar quarter, not necessarily the organisation’s fiscal quarter.

Group by year

SELECT
  EXTRACT(YEAR FROM transaction.trandate) AS report_year,
  COUNT(*) AS transaction_count,
  SUM(transaction.foreigntotal) AS total_amount
FROM transaction
GROUP BY EXTRACT(YEAR FROM transaction.trandate)
ORDER BY report_year

Why months can be missing

Aggregation only returns periods that have matching rows. If April has no transactions, no April row is produced.

For a complete chart axis, either:

  • generate the required date series and outer join the totals; or
  • fill missing months in the application after the query.

The application approach is usually simplest for a short, fixed report. A generated series is useful when the database result itself must contain zero-value rows.