Developer ToolsFree generatorPDF, Excel, Word export

SQL Monthly Demand History Query Generator for Forecasting

Every statistical forecast starts with a clean time series, and most ERPs store transactions, not time series. This generator produces SQL that totals demand by item and month, fills months with no sales with zero, and returns the counts used for demand classification.

Fill in

Generic name. Replace with your ERP table or reporting view.

Use requested date for demand, shipped date for sales history.

Generated code
Download:

PostgreSQL: monthly history with zero-filled gaps

WITH months AS (
  SELECT generate_series(DATE '2024-01-01', DATE '2026-10-01' - INTERVAL '1 month', INTERVAL '1 month')::date AS month_start
),
items AS (
  SELECT DISTINCT item_code AS item
  FROM sales_order_lines
  WHERE item_code LIKE 'BRK-%'
),
monthly AS (
  SELECT item_code AS item,
         DATE_TRUNC('month', ship_date)::date AS month_start,
         SUM(qty_shipped) AS qty
  FROM sales_order_lines
  WHERE ship_date >= DATE '2024-01-01'
    AND ship_date < DATE '2026-10-01'
    AND item_code LIKE 'BRK-%'
  GROUP BY item_code, DATE_TRUNC('month', ship_date)
)
SELECT i.item, m.month_start, COALESCE(d.qty, 0) AS qty
FROM items i
CROSS JOIN months m
LEFT JOIN monthly d ON d.item = i.item AND d.month_start = m.month_start
ORDER BY i.item, m.month_start;

SQL Server: monthly totals

SELECT item_code AS item,
       DATEFROMPARTS(YEAR(ship_date), MONTH(ship_date), 1) AS month_start,
       SUM(qty_shipped) AS qty
FROM sales_order_lines
WHERE ship_date >= '2024-01-01'
  AND ship_date < '2026-10-01'
  AND item_code LIKE 'BRK-%'
GROUP BY item_code, DATEFROMPARTS(YEAR(ship_date), MONTH(ship_date), 1)
ORDER BY item_code, month_start;

ADI inputs per item (PostgreSQL, run on the monthly result)

-- Wrap the first query as monthly_full(item, month_start, qty)
SELECT item,
       COUNT(*) AS total_periods,
       COUNT(*) FILTER (WHERE qty > 0) AS nonzero_periods,
       AVG(qty) FILTER (WHERE qty > 0) AS mean_size,
       STDDEV_SAMP(qty) FILTER (WHERE qty > 0) AS sd_size
FROM monthly_full
GROUP BY item;

Table and column names are generic placeholders, not any specific ERP schema. Map them to your own table or view and remove cancelled, returned and intercompany lines before using the result as demand. Verify the date functions against your database version.

Demand history without hand-written SQL

Want to ask for item history in plain English? Ask for an optional expert review.

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

How to use the generator

Fill in the table and column names from your ERP or reporting view, set the history window and an item filter, then copy the PostgreSQL or SQL Server block that matches your database. Run it, export the result to CSV and load it into Excel, Python or your planning tool. The zero-filled version matters, because missing months silently shorten the series and inflate averages.

Which date and quantity to use

Shipped quantity by ship date shows what customers actually received, but understates demand when you were out of stock. Order quantity by requested date is closer to true demand. Many planners use order lines by requested date for the statistical baseline and keep shipments for service level reporting. Choose one, document it and keep it constant.

  • Remove returns and cancelled lines
  • Exclude intercompany transfers
  • Decide how to treat one-off project orders
  • Keep the unit of measure consistent

Reading the output

You get one row per item per month, including zeros. A part with 36 rows and only 9 non-zero quantities is intermittent, which the third query quantifies with period counts, mean size and standard deviation. Feed those into the ADI and CV2 calculator to pick between smoothing and Croston style methods.

Common mistakes

Grouping by a timestamp column without truncating to the month gives thousands of rows. Using an inner join instead of the cross join drops zero months. Mixing units of measure across lines adds boxes to pieces. Always check that the total over the query equals a trusted sales report for one item before you load everything.

Frequently Asked Questions

Why fill missing months with zero?

A month with no sales is a valid observation of zero demand. If you omit it, the series is shorter, the average is too high and seasonal or intermittent patterns are distorted. Zero-fill before any statistical modelling.

Should demand history use orders or shipments?

Orders by requested date approximate demand better, because shipments are capped by supply. Shipments are easier to get and reconcile to finance. If you use shipments, flag periods with stock-outs and consider adjusting them.

Does this query work on SAP or Oracle tables?

The names are generic. The logic of grouping by item and month applies to any ERP, but you must map to the real tables or views, and review date functions and any client or organization filters for your system.

How do I get weekly history instead?

Replace the month truncation with a week truncation, for example DATE_TRUNC with the week unit in PostgreSQL, and change the generated series interval to one week. Keep the week start convention consistent.

If you would rather ask for this history in plain language and see the query it runs against your ERP, Netray can do that on-prem with read-only access.