Developer ToolsFree generatorPDF, Excel, Word export

SQL Monthly Pivot Query Generator Using Conditional Aggregation

Planners and finance analysts keep asking for the same report: months across the top, items or customers down the side. This generator writes that cross-tab with plain CASE and SUM so it runs in any SQL database.

Fill in

Generated code
Download:

Month columns with conditional aggregation

SELECT
    item_code,
    SUM(CASE WHEN MONTH(transaction_date) = 1  THEN quantity ELSE 0 END) AS m01_jan,
    SUM(CASE WHEN MONTH(transaction_date) = 2  THEN quantity ELSE 0 END) AS m02_feb,
    SUM(CASE WHEN MONTH(transaction_date) = 3  THEN quantity ELSE 0 END) AS m03_mar,
    SUM(CASE WHEN MONTH(transaction_date) = 4  THEN quantity ELSE 0 END) AS m04_apr,
    SUM(CASE WHEN MONTH(transaction_date) = 5  THEN quantity ELSE 0 END) AS m05_may,
    SUM(CASE WHEN MONTH(transaction_date) = 6  THEN quantity ELSE 0 END) AS m06_jun,
    SUM(CASE WHEN MONTH(transaction_date) = 7  THEN quantity ELSE 0 END) AS m07_jul,
    SUM(CASE WHEN MONTH(transaction_date) = 8  THEN quantity ELSE 0 END) AS m08_aug,
    SUM(CASE WHEN MONTH(transaction_date) = 9  THEN quantity ELSE 0 END) AS m09_sep,
    SUM(CASE WHEN MONTH(transaction_date) = 10 THEN quantity ELSE 0 END) AS m10_oct,
    SUM(CASE WHEN MONTH(transaction_date) = 11 THEN quantity ELSE 0 END) AS m11_nov,
    SUM(CASE WHEN MONTH(transaction_date) = 12 THEN quantity ELSE 0 END) AS m12_dec,
    SUM(quantity) AS period_total
FROM dbo.inventory_transactions
WHERE transaction_date >= '2025-01-01'
  AND transaction_date <  '2026-01-01'
  AND transaction_type = 'ISSUE'
GROUP BY item_code
ORDER BY period_total DESC;

Average monthly usage and months with activity

WITH monthly AS (
    SELECT
        item_code AS grp,
        YEAR(transaction_date) AS yr,
        MONTH(transaction_date) AS mth,
        SUM(quantity) AS month_value
    FROM dbo.inventory_transactions
    WHERE transaction_date >= '2025-01-01'
      AND transaction_date <  '2026-01-01'
      AND transaction_type = 'ISSUE'
    GROUP BY item_code, YEAR(transaction_date), MONTH(transaction_date)
)
SELECT
    grp,
    COUNT(*) AS months_with_activity,
    SUM(month_value) AS total_value,
    AVG(month_value) AS avg_active_month,
    MIN(month_value) AS min_month,
    MAX(month_value) AS max_month
FROM monthly
GROUP BY grp
ORDER BY total_value DESC;

Unpivoted monthly rows for charting

SELECT
    item_code,
    YEAR(transaction_date) AS yr,
    MONTH(transaction_date) AS mth,
    SUM(quantity) AS month_value
FROM dbo.inventory_transactions
WHERE transaction_date >= '2025-01-01'
  AND transaction_date <  '2026-01-01'
  AND transaction_type = 'ISSUE'
GROUP BY item_code, YEAR(transaction_date), MONTH(transaction_date)
ORDER BY item_code, yr, mth;

The first block assumes a window of at most 12 months in one calendar year. For a range spanning two years, use the third block or add YEAR to the CASE test, otherwise January of both years is merged. MONTH() and YEAR() are SQL Server, MySQL and PostgreSQL friendly; on Oracle use EXTRACT(MONTH FROM col). Date literals may need your engine's format.

Optional report automation

Tell us which ERP cross-tab you rebuild each month and we will outline how to automate it.

Optional. The tool and every export work without this. No spam.

How to use the generator

Enter the table or view, the column that labels each row, the date column and the value to add up. Set the start date inclusive and the end date exclusive, and add any filter such as the transaction type. Copy the first block for the cross-tab. Run it on a small group first and compare one row with the ERP's own inquiry screen. If it matches, widen the filter and paste the result into Excel for charts or a rolling forecast.

Why conditional aggregation

SQL Server has a PIVOT operator and some engines have crosstab extensions, but they differ between databases and need the column list in advance. Conditional aggregation uses SUM over a CASE per month, which is plain ANSI SQL, easy to read and easy to adjust, for example to add a quantity and a value pair. The cost is a wide query to maintain, which is why a generator helps. The date range should use a half-open interval so that the last day is never lost when the column has a time part.

Reading the result

The period total column sorts the biggest movers first. Use the second block when you need average usage for reorder points: it averages only months with activity, so an item used in 3 of 12 months looks very different from an item used every month. Compare average of active months with the total divided by the number of months to see lumpiness. That gap often explains why a safety stock calculation based on a flat average fails for lumpy demand.

Common mistakes

The most common error is a range crossing two calendar years in the first block, which merges months. Others are using BETWEEN with datetime values, which drops the final day, forgetting to filter reversals or voided rows, and summing a signed quantity where you meant issues only. Check the filter against a transaction type list in your data dictionary before trusting the totals.

Frequently Asked Questions

Why not use the PIVOT operator?

PIVOT works in SQL Server and Oracle but with different syntax and a fixed column list. Conditional aggregation is portable, easier to extend with extra measures and just as fast in most ERP reporting queries.

Can I do a rolling 12 months instead of a calendar year?

Yes. Use the unpivoted third block with a date range of the last 12 months, then pivot in Excel. Or replace the MONTH test with a month offset from the end date computed in your engine.

How do I avoid dropping the last day of the range?

Use a half-open range: on or after the start and strictly before the day after the end. Avoid BETWEEN with datetime columns because midnight ends the range.

Will this be slow on big tables?

It scans the rows in the date window once. An index on the date column, ideally including the group and value columns, helps. Filter by date range first and avoid wrapping the date column in functions in the WHERE clause.

If you want these cross-tabs scheduled from your ERP into a shared sheet or dashboard, we can build and maintain that for you.