Premium · Grocery chain, 10 stores, fiscal 2024 and 2025

Retail Data Warehouse

Two fiscal years of a grocery chain, from the shelf to the receipt

2,790,852 rows in a star schema of 9 dimensions and 5 facts: 10 stores, 240 products in 31 subcategories, 36,000 loyalty members, 1,163,432 store-item-days, 372,583 baskets, 1,101,202 receipt lines and 10,601 returns, fiscal 2024 and 2025 (4 February 2024 to 31 January 2026) on the NRF 4-5-4 calendar.

Why it exists

Public retail data is either a star schema with nothing going on inside it, or sales with no shelf behind them. This warehouse is both halves of a real chain joined up. Every receipt line is a unit that left a shelf that was really stocked: demand is built factor by factor (season, trading events, price, promotion, stock-up dip afterwards, cannibalisation by a sibling on promotion), the chain orders from its own imperfect forecast, deliveries come on each store's cycle and arrive short in a supplier shortage, fresh food spoils, and what the shelf did not have is recorded as a lost sale. Then the units sold are rung up into baskets, carded to members whose profile was in force that day, and some come back as returns. The ground truth is delivered alongside, so a model can be scored against it.

Uses

What it is built for

  • Star schema and BI

    a Kimball-style warehouse with two type 2 slowly changing dimensions, a fiscal calendar, a periodic snapshot fact, transaction facts at two grains and a factless range table. Power BI, Tableau, Looker, dbt, SQL courses and interview practice. schema.sql has every key; queries.sql has 17 worked analyses.

  • Demand forecasting

    1,163,432 daily series with seasonality, holidays, promotions, price changes, launches, delistings and a new store. demand_units is true demand, not sales capped by stock, and the chain's own forecast is there to beat (WAPE 71%).

  • Promotion and price analytics

    uplift, pantry loading, cannibalisation and elasticity, with the true factors (promo_lift, pantry_dip, cannibal_effect, price_effect, true_elasticity) to check an estimate against.

  • Inventory and supply chain

    stock-flow identities hold on every row; out-of-stocks, lost sales, fresh waste, safety stock and a supplier shortage to study.

  • Customer analytics

    RFM, churn, basket affinity and market-basket rules, home-store loyalty and tier changes.

  • Returns and fraud-style exercises

    returns tied to the exact line and price paid.

Project kit

A portfolio project you can check yourself

A brief from the Commercial Director of a ten-store grocery chain, 18 questions from warm-up to stretch, and an answer key to grade against. Every answer was worked out twice on these files, in DuckDB SQL and separately in pandas, and the kit was written only when the two agreed.

The brief

Two-year trading review for the board (fiscal 2024 and 2025)

From the Commercial Director of a ten-store grocery chain · fiscal 2024 and 2025 (4 February 2024 to 31 January 2026)

  • BRIEF.mdthe request, from the Commercial Director of a ten-store grocery chain
  • QUESTIONS.md18 questions, each asking for one value in a stated form
  • load.sqlevery table into DuckDB with the right types
  • check.pygrades your answers, names the misreading a wrong one matches
  • solutions/the answer key and the SQL behind every answer
  • WRITEUP_TEMPLATE.mdthe two pages a hiring manager reads

What the checker says

$ python kit/check.py kit/my_answers.csv
  Q05  wrong  that is the answer when you include the store that opened in 2025

A wrong answer is named for the mistake it matches, such as the wrong rows in a denominator or a mean where a median is asked, without giving the answer away. --reveal shows one answer and its SQL when you want it.

Warm-up
4
Core
8
Stretch
6
Read all 18 questions

Warm-up

  1. Q01. Fiscal 2025 sales. What were net sales (net_amount on receipt lines) in fiscal 2025?
  2. Q02. Gross margin. What was the gross margin rate over both years: margin_amount over net_amount on receipt lines?
  3. Q03. Baskets. How many baskets (transactions) were rung up in fiscal 2024?
  4. Q04. Loyalty penetration. What share of baskets scanned a loyalty card?

Core

  1. Q05. Like-for-like growth. What was net sales growth from fiscal 2024 to fiscal 2025 for the stores open throughout both years (opened before 4 February 2024)?
  2. Q06. Average basket. What was the average net value of a basket in fiscal 2025?
  3. Q07. Sales on promotion. What share of net sales over both years came from receipt lines sold on a promotion?
  4. Q08. Empty shelves. On open store-item-days, what share ended out of stock (out_of_stock true)?
  5. Q09. Lost demand. What share of demand (demand_units) went unmet as lost sales (lost_units), over both years?
  6. Q10. Best margin department. Which department has the highest gross margin rate (margin_amount over net_amount)?
  7. Q11. Busiest day of the week. Which day of the week has the highest net sales over both years?
  8. Q12. Where returns come from. Which department has the highest return rate: units returned (fact_return.quantity) over units sold (fact_sales_line.quantity)?

Stretch

  1. Q13. Promotion uplift, as measured. For each store and product, divide the average units sold on open promotion days by the average on open regular days. What is the median of that ratio over the store-products that have both (and sell on regular days)?
  2. Q14. The chain's forecast. On open store-item-days, what is the forecast's WAPE: the sum of the absolute difference between forecast_units and demand_units, over the sum of demand_units?
  3. Q15. The late-summer shortage. Which subcategory lost the largest share of its demand (lost_units over demand_units) in August and September 2024?
  4. Q16. What Gold members spend. What were fiscal 2025 net sales on baskets whose member held the Gold tier at the time of the basket (the profile on the basket's customer_key)?
  5. Q17. Fresh shrink. In the Fresh department, what share of units received (received_units) were wasted (waste_units), over both years?
  6. Q18. Pasta and sauce. Over every basket, what is the lift of 'Pasta & rice' with 'Sauces': the share of baskets holding both, over the product of the shares holding each?

In the data

What the full files show

Computed from every row of the full dataset when it was packaged, not drawn as a target. The free preview is a slice of the same data.

Weekly sales, at regular price and on promotion

$0$100,000$200,000Feb 2024Jun 2024Oct 2024Feb 2025Jun 2025Oct 2025
  • Regular price
  • On promotion
Net sales per fiscal week from fact_sales_line, split by whether the line carried a promotion. Holidays, seasons, the August 2024 bottler shortage and the new store in April 2025 all show.

Demand lost to an empty shelf, by price status

  • regular1.3%
  • promotion7.2%
  • clearance6.5%
Lost units over true demand in fact_store_item_day. The chain's forecast under-plans promotions, so they run out; the truth (demand_units) is in the file, not only what sold.

Promotion lift, by the product's true price elasticity

  • below -2.23.71x
  • -2.2 to -1.82.88x
  • -1.8 to -1.32.4x
  • above -1.31.95x
Mean promo_lift on promotion days, grouped by dim_product.true_elasticity. Price-sensitive products lift more, which is what an elasticity or uplift model should recover.

Demand lost to an empty shelf, by department

  • Grocery4.3%
  • Fresh1.1%
  • Household4.7%
  • Frozen3.6%
  • General merchandise2.7%
  • Health & beauty2.4%
Lost units as a share of true demand, from fact_store_item_day. Only fresh food spoils: Fresh wastes 14.8% of the units it sells (waste_units).

Answer key

The truth, in its own columns

What a model, a control chart or an analyst is trying to find ships next to the data, so you can score an answer instead of guessing at it. Each line is measured from the full files.

  • fact_store_item_day.baseline_unitsDemand the item would have that day at its regular price, before any effect.0 to 34.31, mean 1.2
  • fact_store_item_day.price_effectThe multiplier from the shelf price against the reference, through the product's elasticity.0.49 to 1.33, mean 0.92
  • fact_store_item_day.promo_liftThe multiplier from a promotion in force; 1 when there is none.1 to 9.72, mean 1.19
  • fact_store_item_day.pantry_dipThe dip after a promotion in stock-up categories, as shoppers use what they bought; 1 when none.0.72 to 1, mean 0.98
  • fact_store_item_day.cannibal_effectThe multiplier while a sibling product is on promotion.0.46 to 1, mean 0.88
  • fact_store_item_day.expected_unitsThe product of those factors: the demand to expect.0 to 77.9, mean 1.15
  • fact_store_item_day.demand_unitsTrue demand drawn around the expectation, including units nobody could buy.0 to 77, mean 1.15
  • fact_store_item_day.lost_unitsDemand lost because the shelf was empty: the gap between demand and sales.0 to 52, mean 0.034
  • dim_product.true_elasticityThe product's true price elasticity, to check an estimate against.-2.84 to -0.48, mean -1.4
  • fact_sales_transaction.basket_missionWhy the basket was bought (breakfast, party, cookout and so on), for market-basket work.9 values: mixed, dinner, breakfast…

How it behaves

Measured on the files you download

Each of these is computed from the delivered rows when the dataset is packaged, not written as a target.

  • Sales $7,463,018 over 1,303,240 units at a 33.2% gross margin; 21% of sales on promotion.
  • Like-for-like growth +4.0% in the stores open both years; a new store opened 2025-04-06, first two weeks at 39% of its last 90 days.
  • Seasons and events: ice cream July/January 2.0x; confectionery before Halloween 3.7x a normal day. Saturday is the busiest day, Tuesday the quietest.
  • Promotions lift demand 2.36x the baseline on promotion days, and price-sensitive products lift more: 2.16x for elasticity below -1.8, 1.48x above -1.3 (20-30% off, no display or flyer). A sibling on promotion takes sales: 0.82x baseline while a sibling is promoted. Stock-up categories dip after a promotion and recover.
  • The forecast under-plans promotions, so they run out: lost share of demand clearance 6.5%, promotion 7.2%, regular 1.3%. Shelves are out of stock on 1.4% of item-days; 3.0% of demand is lost overall.
  • A bottler's shortage in August and September 2024: 43% of demand lost in the shortage, 5% outside.
  • Fresh shrink: about one fresh unit in eight shrinks.
  • Baskets: median 2 lines of the tracked range; hypermarket 3.6, neighbourhood 1.8, supermarket 2.8 lines per basket by format. 56% of baskets scan a loyalty card; 87% of carded baskets are at the member's home store.
  • Shopping missions (breakfast, dinner, party, cookout, baking, home care, personal care, school and gifts, or a mixed shop) put complements in the same basket: median lift 3.5 within a mission, 0.5 across, so market-basket rules find pasta with sauce and crisps with soft drinks. basket_mission is delivered as the answer key.
  • Price elasticity is recoverable from the regular price changes: by subcategory, correlation 0.86 with the truth, median error 0.12.
  • Returns: 0.96% of lines; Fresh 0.41%, Frozen 0.28%, General merchandise 5.50%, Grocery 0.21%, Health & beauty 1.63%, Household 0.76%.

Audit

117 of 117 checks pass

Re-run on the delivered files by an independent script with plain pandas. The results ship in INTEGRITY.json.

  • every primary key is unique and every foreign key resolves;
  • the 4-5-4 calendar: 52 Sunday-to-Saturday weeks a year, 4-5-4 periods, closures on Thanksgiving and Christmas Day;
  • both slowly changing dimensions chain without gaps or overlaps with one current version, and every fact carries the version in force at its time;
  • prices: every list and shelf price sits on a price point (x.x9 under $10, .49 or .99 above), every price change moves the way its reason says; a price cut or clearance lands on the next price point down, a multi-buy or buy one get one free sells a unit for its share of the deal, to the cent;
  • the shelf: close = open + received - sold - waste on every row, tomorrow opens with today's close, demand = sold + lost, a sale is lost only when the shelf ran empty, only fresh food spoils;
  • demand is the product of its delivered factors and unbiased around it; the effects above are measured, not assumed;
  • baskets: totals are the sums of their lines, lines ring up exactly the units the shelf sold, at that day's price and promotion; cards only after the member joined; returns after their sale, of what was bought, at what was paid.
All 117 checks
  • ✓ dim_date.date_id unique: 728 rows
  • ✓ dim_store.store_id unique: 10 rows
  • ✓ dim_subcategory.subcategory_id unique: 31 rows
  • ✓ dim_product.product_id unique: 240 rows
  • ✓ dim_product_price_history.price_version_id unique: 896 rows
  • ✓ dim_promotion.promotion_id unique: 2,493 rows
  • ✓ dim_customer.customer_id unique: 36,000 rows
  • ✓ dim_customer_history.customer_key unique: 93,958 rows
  • ✓ dim_store_range.position_id unique: 1,825 rows
  • ✓ fact_store_day.store_day_id unique: 6,853 rows
  • ✓ fact_store_item_day.store_item_day_id unique: 1,163,432 rows
  • ✓ fact_sales_transaction.transaction_id unique: 372,583 rows
  • ✓ fact_sales_line.line_id unique: 1,101,202 rows
  • ✓ fact_return.return_id unique: 10,601 rows
  • ✓ dim_product.subcategory_id -> dim_subcategory: 0 orphans of 240
  • ✓ dim_product_price_history.product_id -> dim_product: 0 orphans of 896
  • ✓ dim_promotion.product_id -> dim_product: 0 orphans of 2,493
  • ✓ dim_customer.home_store_id -> dim_store: 0 orphans of 36,000
  • ✓ dim_customer_history.customer_id -> dim_customer: 0 orphans of 93,958
  • ✓ dim_store_range.store_id -> dim_store: 0 orphans of 1,825
  • ✓ dim_store_range.product_id -> dim_product: 0 orphans of 1,825
  • ✓ fact_store_day.store_id -> dim_store: 0 orphans of 6,853
  • ✓ fact_store_day.date_id -> dim_date: 0 orphans of 6,853
  • ✓ fact_store_item_day.position_id -> dim_store_range: 0 orphans of 1,163,432
  • ✓ fact_store_item_day.store_day_id -> fact_store_day: 0 orphans of 1,163,432
  • ✓ fact_store_item_day.date_id -> dim_date: 0 orphans of 1,163,432
  • ✓ fact_store_item_day.product_id -> dim_product: 0 orphans of 1,163,432
  • ✓ fact_store_item_day.promotion_id -> dim_promotion: 0 orphans of 163,513
  • ✓ fact_store_item_day.price_version_id -> dim_product_price_history: 0 orphans of 1,163,432
  • ✓ fact_sales_transaction.store_day_id -> fact_store_day: 0 orphans of 372,583
  • ✓ fact_sales_transaction.customer_id -> dim_customer: 0 orphans of 208,519
  • ✓ fact_sales_transaction.customer_key -> dim_customer_history: 0 orphans of 208,519
  • ✓ fact_sales_line.transaction_id -> fact_sales_transaction: 0 orphans of 1,101,202
  • ✓ fact_sales_line.store_item_day_id -> fact_store_item_day: 0 orphans of 1,101,202
  • ✓ fact_sales_line.product_id -> dim_product: 0 orphans of 1,101,202
  • ✓ fact_sales_line.promotion_id -> dim_promotion: 0 orphans of 282,999
  • ✓ fact_sales_line.customer_key -> dim_customer_history: 0 orphans of 616,780
  • ✓ fact_sales_line.date_id -> dim_date: 0 orphans of 1,101,202
  • ✓ fact_return.line_id -> fact_sales_line: 0 orphans of 10,601
  • ✓ a store ranges a product once
  • ✓ the calendar is every day of fiscal 2024 and 2025
  • ✓ fiscal weeks are 7 days from a Sunday, 52 a year
  • ✓ periods follow 4-5-4 in every quarter
  • ✓ fiscal_week_start is the Sunday of the week
  • ✓ date_key is YYYYMMDD
  • ✓ stores close on Thanksgiving and Christmas Day only
  • ✓ nothing is sold on a day the stores are closed
  • ✓ price history: versions chain without gaps or overlaps, one current
  • ✓ price history: versions are numbered in order
  • ✓ customer history: versions chain without gaps or overlaps, one current
  • ✓ customer history: versions are numbered in order
  • ✓ a product's first price is set on its launch or before the window
  • ✓ a member's first profile starts the day they joined
  • ✓ every new price moves the way its reason says
  • ✓ list prices sit on price points, the first one the product's base price: 100% of base prices end in 9
  • ✓ a postcode is recorded when a member moves, and only then
  • ✓ the price version on every item-day is the one in force that day
  • ✓ the shelf's regular price is the version's price in the store's price zone, on a price point
  • ✓ the discount is the share off the regular price, as sold
  • ✓ a promotion on an item-day is that product's, running that day
  • ✓ a price cut is the promotion's cut, on the next price point down
  • ✓ a multi-buy or buy one get one sells a unit for its share of the deal, to the cent
  • ✓ every item-day inside a promotion carries it
  • ✓ a product's promotions never overlap
  • ✓ promotions are only for promotable subcategories
  • ✓ price status agrees with the discount
  • ✓ clearance marks down 40% to the next price point
  • ✓ clearance is the last three weeks before a product is delisted
  • ✓ every promotion is a standard deal, and its depth is what the deal takes off a unit: 25% off 402, 20% off 397, 4 for 3 374, 3 for 2 352, buy one get one free 284, 10% off 234, 15% off 228, 30% off 222
  • ✓ stock flows: close = open + received - sold - waste
  • ✓ demand = sold + lost: nothing sells that is not on the shelf
  • ✓ tomorrow opens with what today closed with
  • ✓ a sale is lost only when the shelf ran empty
  • ✓ only fresh food spoils
  • ✓ sales and cost amounts are units times price
  • ✓ expected demand is the product of its factors
  • ✓ demand is drawn around its expectation (unbiased in every price status): clearance -0.2%, promotion +0.2%, regular +0.1%
  • ✓ a promotion lifts demand: 2.36x the baseline on promotion days
  • ✓ more price-sensitive products lift more at the same discount: 2.16x for elasticity below -1.8, 1.48x above -1.3 (20-30% off, no display or flyer)
  • ✓ after a stock-up promotion, demand dips then recovers
  • ✓ a sibling on promotion takes some of a product's sales: 0.82x baseline while a sibling is promoted
  • ✓ Saturday is the busiest day, Tuesday the quietest
  • ✓ summer products sell in summer, winter ones in winter: ice cream July/January 2.0x
  • ✓ holiday lead-ups lift their products: confectionery before Halloween 3.7x a normal day
  • ✓ the bottler's shortage empties the shelves: 43% of demand lost in the shortage, 5% outside
  • ✓ promotions run out more than regular days (the forecast under-plans the lift): clearance 6.5%, promotion 7.2%, regular 1.3%
  • ✓ shelves are out of stock on a few percent of item-days, as in real grocery: 1.4%
  • ✓ fresh food waste is in the range grocers report: about one fresh unit in eight shrinks
  • ✓ like-for-like stores grow year on year: +4.0% in the stores open both years
  • ✓ the new store opens on its date and ramps up: opened 2025-04-06, first two weeks at 39% of its last 90 days
  • ✓ every basket has lines, and its totals are the sums of them
  • ✓ the lines ring up exactly the units the shelf sold
  • ✓ a line is in a basket of its own store and day, at its time
  • ✓ a line is charged the shelf price that day, with its promotion
  • ✓ line amounts: net = quantity x price, discount = gross - net
  • ✓ a line carries its basket's loyalty key
  • ✓ a store-day's lines and baskets add up
  • ✓ sales are rung up in opening hours, on the store-day's date
  • ✓ a card is used only after its member joined: 56% of baskets scan a card
  • ✓ members shop mostly at their home store: 87%
  • ✓ the loyalty key on a basket is the member's profile in force then
  • ✓ a basket without a card has no customer
  • ✓ hypermarket baskets are bigger than neighbourhood ones: hypermarket 3.6, neighbourhood 1.8, supermarket 2.8
  • ✓ products on the same shopping mission are bought together: median lift 3.5 within a mission, 0.5 across
  • ✓ a basket's mission is where its lines' missions point
  • ✓ price elasticity is recoverable from regular price changes: by subcategory, correlation 0.86 with the truth, median error 0.12
  • ✓ a return follows its sale within 30 days, in opening hours, inside the window
  • ✓ a return is of what was bought, refunded at what was paid
  • ✓ general merchandise comes back far more than groceries: Fresh 0.41%, Frozen 0.28%, General merchandise 5.50%, Grocery 0.21%, Health & beauty 1.63%, Household 0.76%
  • ✓ only a resaleable reason goes back on the shelf
  • ✓ every product name is unique
  • ✓ names read as brand, product and size, never a pack word
  • ✓ a brand stays in its own aisles (no farm brand selling toys): 11 brands
  • ✓ a bigger pack costs more, and less per unit: small 0.70, regular 1.03, large 1.45, family 1.81
  • ✓ payment methods do not overlap
  • ✓ click and collect is paid online, never in cash
  • ✓ only general merchandise is returned for size
  • ✓ rows: dim_customer 36,000, dim_customer_history 93,958, dim_date 728, dim_product 240, dim_product_price_history 896, dim_promotion 2,493, dim_store 10, dim_store_range 1,825, dim_subcategory 31, fact_return 10,601, fact_sales_line 1,101,202, fact_sales_transaction 372,583, fact_store_day 6,853, fact_store_item_day 1,163,432
  • ✓ sales: $7,463,018 over 1,303,240 units; gross margin 33.2%
  • ✓ demand: 1,342,912 units wanted, 39,672 lost to empty shelves (3.0%)
  • ✓ baskets: 372,583, median 2 lines of the tracked range, 56% with a card
  • ✓ returns: 10,601 (0.96% of lines)

Tables

14 tables, 2,790,852 rows

Every table in the zip with what it holds, its rows and its columns. The bars are on a log scale, so the small reference tables still show.

  • fact_store_item_dayOne row per ranged product per store per day: price status, discount, true demand and every factor in it, the chain's forecast, opening stock, deliveries, sales, lost sales, waste, closing stock, orders1,163,432 rows · 38 cols
  • fact_sales_lineReceipt lines: product, quantity, regular and paid price, promotion, gross, discount, net, cost and margin, the basket's loyalty key1,101,202 rows · 19 cols
  • fact_sales_transactionBaskets: receipt number, time, till, channel, payment, the loyalty card and the member's profile version in force, and totals372,583 rows · 17 cols
  • dim_customer_historyType 2 slowly changing dimension of member profiles: tier changes and moves, with validity and the current flag93,958 rows · 10 cols
  • dim_customerLoyalty members: home store, joined date, birth year, household size, shopper type36,000 rows · 7 cols
  • fact_returnReturns against the line they came from: when, how many, why, refund and whether it went back on the shelf10,601 rows · 10 cols
  • fact_store_dayOne row per store per day: open or closed, receipt lines and baskets6,853 rows · 7 cols
  • dim_promotionPromotions: mechanic and the advertised deal (10 to 30% off, 3 for 2, 4 for 3, buy one get one free), the share it takes off a unit, weeks, display and flyer support2,493 rows · 12 cols
  • dim_store_rangeWhich store ranges which product (the planogram), with safety stock and display minimum1,825 rows · 6 cols
  • dim_product_price_historyType 2 slowly changing dimension of regular prices: every version with valid_from, valid_to, is_current and why it changed896 rows · 8 cols
  • dim_dateEvery day of fiscal 2024 and 2025 on the NRF 4-5-4 calendar: fiscal week, period and quarter, holidays, trading events, pay days, the two days stores close728 rows · 17 cols
  • dim_productSKUs: brand or private label, pack, base price (on a price point) and unit cost, velocity, the product's true price elasticity, launch and delisting dates240 rows · 16 cols
  • dim_subcategory31 subcategories in 6 departments: seasonality, trading event, reference price, price elasticity, daily spoilage, return rate, whether promoted and stocked up on31 rows · 15 cols
  • dim_storeStores: format (hypermarket, supermarket, neighbourhood), region, selling area, opening date (one opens mid-window), traffic, price zone, delivery cycle and lead time10 rows · 12 cols

Explore

Every table, profiled

Each column's type, spread, empties and most common values, measured from the full CSVs. Switch to the first rows to see the data as it sits in the file.

fact_store_item_day.csv

1,163,432 rows · 38 columns · 8 foreign keys

store_item_day_idprimary key
unique on every row
1.16M distinctno empties
position_idforeign key
points to dim_store_range.position_id
1,818 distinctno empties
store_idforeign key
points to dim_store.store_id
10 distinctno empties
product_idforeign key
points to dim_product.product_id
240 distinctno empties
subcategory_idforeign key
points to dim_subcategory.subcategory_id
31 distinctno empties
datedate
Feb 2024Jan 2026
2024-02-04 to 2026-01-31no empties
date_idforeign key
points to dim_date.date_id
728 distinctno empties
store_day_idforeign key
points to fact_store_day.store_day_id
6,853 distinctno empties
store_openyes / no
true · 99%false · 0.6%
no empties
regular_pricenumber
1.3926.99
mean 6.74median 4.891.19 to 31.99no empties
promotion_idforeign key
points to dim_promotion.promotion_id
2,493 distinct86% empty
price_statuscategory
  • regular
    86%
  • promotion
    14%
  • clearance
    0.2%
3 valuesno empties
shelf_pricenumber
1.226.99
mean 6.48median 4.690.7 to 31.99no empties
discount_depthnumber
00.5
mean 0.038median 00 to 0.5no empties
on_displayyes / no
true · 4.8%false · 95%
no empties
in_flyeryes / no
true · 6.0%false · 94%
no empties
baseline_unitsnumber
07.82
mean 1.2median 0.80 to 34.31no empties
price_effectnumber
0.591.28
mean 0.92median 0.920.49 to 1.33no empties
promo_liftnumber
15.39
mean 1.19median 11 to 9.72no empties
pantry_dipnumber
0.721
mean 0.98median 10.72 to 1no empties
cannibal_effectnumber
0.531
mean 0.88median 0.880.46 to 1no empties
expected_unitsnumber
09.63
mean 1.15median 0.70 to 77.9no empties
demand_unitsnumber
011
mean 1.15median 10 to 77no empties
forecast_unitsnumber
08.04
mean 1.05median 0.640 to 66.52no empties
order_up_tonumber
239
mean 7.27median 62 to 295no empties
supplier_fillnumber
0.321
mean 0.98median 10.05 to 1no empties
on_hand_opennumber
029
mean 5median 40 to 209no empties
received_unitsnumber
015
mean 1.17median 00 to 139no empties
units_soldnumber
010
mean 1.12median 10 to 77no empties
lost_unitsnumber
02
mean 0.034median 00 to 52no empties
waste_unitsnumber
011
mean 0.056median 00 to 11no empties
on_hand_closenumber
029
mean 4.99median 40 to 209no empties
ordered_unitsnumber
015
mean 1.21median 00 to 139no empties
out_of_stockyes / no
true · 1.4%false · 99%
no empties
receipt_linesnumber
08
mean 0.95median 10 to 77no empties
sales_amountnumber
079.95
mean 6.41median 1.990 to 462no empties
cost_amountnumber
057.15
mean 4.29median 1.270 to 647.6no empties
price_version_idforeign key
points to dim_product_price_history.price_version_id
741 distinctno empties

Keys

Every join resolves

37 foreign keys, 18,648,182 references checked against the table each one points at. None points at a row that does not exist.

dim_customer

  • home_store_iddim_store.store_id0 orphans

dim_customer_history

  • customer_iddim_customer.customer_id0 orphans

dim_product

  • subcategory_iddim_subcategory.subcategory_id0 orphans

dim_product_price_history

  • product_iddim_product.product_id0 orphans

dim_promotion

  • product_iddim_product.product_id0 orphans
  • subcategory_iddim_subcategory.subcategory_id0 orphans

dim_store_range

  • store_iddim_store.store_id0 orphans
  • product_iddim_product.product_id0 orphans
  • subcategory_iddim_subcategory.subcategory_id0 orphans

fact_return

  • line_idfact_sales_line.line_id0 orphans
  • store_iddim_store.store_id0 orphans
  • product_iddim_product.product_id0 orphans
  • subcategory_iddim_subcategory.subcategory_id0 orphans

fact_sales_line

  • store_item_day_idfact_store_item_day.store_item_day_id0 orphans
  • store_day_idfact_store_day.store_day_id0 orphans
  • transaction_idfact_sales_transaction.transaction_id0 orphans
  • store_iddim_store.store_id0 orphans
  • product_iddim_product.product_id0 orphans
  • subcategory_iddim_subcategory.subcategory_id0 orphans
  • date_iddim_date.date_id0 orphans
  • customer_keydim_customer_history.customer_key0 orphans
  • promotion_iddim_promotion.promotion_id0 orphans

fact_sales_transaction

  • store_day_idfact_store_day.store_day_id0 orphans
  • store_iddim_store.store_id0 orphans
  • date_iddim_date.date_id0 orphans
  • customer_iddim_customer.customer_id0 orphans
  • customer_keydim_customer_history.customer_key0 orphans

fact_store_day

  • store_iddim_store.store_id0 orphans
  • date_iddim_date.date_id0 orphans

fact_store_item_day

  • position_iddim_store_range.position_id0 orphans
  • store_iddim_store.store_id0 orphans
  • product_iddim_product.product_id0 orphans
  • subcategory_iddim_subcategory.subcategory_id0 orphans
  • date_iddim_date.date_id0 orphans
  • store_day_idfact_store_day.store_day_id0 orphans
  • promotion_iddim_promotion.promotion_id0 orphans
  • price_version_iddim_product_price_history.price_version_id0 orphans

In the zip

What you get

  • CSV and Parquet: one file per table, with a header row and ISO dates.
  • README.md: what each table holds, how the data behaves (measured), what was checked and what to know.
  • ANSWER_KEY.md: what happened at the plant, when, where it shows and the query that finds it.
  • INTEGRITY.json: the 117 audit checks and their results.
  • RECIPE.json: the Misata blueprint that made these exact rows, seed 20240204.
  • schema.sql: DDL with every primary and foreign key.
  • queries.sql: worked analyses, each tested against the data in DuckDB.
  • kit/: the project kit: the brief, 18 questions, load.sql for DuckDB, check.py, the answer key with its SQL, and the write-up template.
All 44 files
  • data/dim_date.csv64 KB
  • parquet/dim_date.parquet9 KB
  • data/dim_store.csv819 B
  • parquet/dim_store.parquet2 KB
  • data/dim_subcategory.csv3 KB
  • parquet/dim_subcategory.parquet4 KB
  • data/dim_product.csv32 KB
  • parquet/dim_product.parquet11 KB
  • data/dim_product_price_history.csv47 KB
  • parquet/dim_product_price_history.parquet8 KB
  • data/dim_promotion.csv211 KB
  • parquet/dim_promotion.parquet19 KB
  • data/dim_customer.csv1.8 MB
  • parquet/dim_customer.parquet224 KB
  • data/dim_customer_history.csv5.7 MB
  • parquet/dim_customer_history.parquet568 KB
  • data/dim_store_range.csv36 KB
  • parquet/dim_store_range.parquet10 KB
  • data/fact_store_day.csv230 KB
  • parquet/fact_store_day.parquet38 KB
  • data/fact_store_item_day.csv191.2 MB
  • parquet/fact_store_item_day.parquet19.8 MB
  • data/fact_sales_transaction.csv42.6 MB
  • parquet/fact_sales_transaction.parquet5.7 MB
  • data/fact_sales_line.csv110.5 MB
  • parquet/fact_sales_line.parquet14.9 MB
  • data/fact_return.csv720 KB
  • parquet/fact_return.parquet130 KB
  • README.md12 KB
  • INTEGRITY.json18 KB
  • RECIPE.json85 KB
  • schema.sql9 KB
  • queries.sql10 KB
  • kit/BRIEF.md2 KB
  • kit/QUESTIONS.md4 KB
  • kit/README.md1 KB
  • kit/WRITEUP_TEMPLATE.md2 KB
  • kit/check.py3 KB
  • kit/load.sql2 KB
  • kit/my_answers.csv100 B
  • kit/questions.json5 KB
  • kit/solutions/ANSWER_KEY.md10 KB
  • kit/solutions/key.json8 KB
  • kit/solutions/solutions.sql4 KB

Before you use it

Things to know

  • Tracked range: 240 products stand for the chain's assortment, so a basket holds only the lines from that range (median 2); a real basket would have more lines of untracked products.
  • Fiscal years are named for the calendar year they start in (fiscal 2024 runs 4 February 2024 to 1 February 2025).
  • Money is in US dollars. true_elasticity, baseline_units and the demand factors are ground truth no retailer could see; use sales and prices alone for a realistic exercise, and the truth to score it.
  • Stores, brands, products, suppliers and members are invented; no real retailer, brand or person is represented.

Questions

Before you buy

Is this real data?
No. This is synthetic data generated by software. No row describes a real person, company, patient, store, machine or transaction. The patterns are modelled to be realistic and the statistics quoted are measured on these files, but they do not describe any real population or market. Use it for learning, testing, demos, benchmarks and prototyping, not as evidence about the real world. Provided as is, without warranty.
What do I get?
One zip of 109.7 MB: 14 linked tables and 2,790,852 rows as CSV, Parquet, SQL DDL (schema.sql), worked SQL analyses (queries.sql), a README of what each table holds and how the data behaves, INTEGRITY.json with the 117 audit checks, and RECIPE.json, the Misata blueprint that made these exact rows (seed 20240204).
Can I try it before buying?
Yes. The free preview (597 KB) is a slice of the same data with the keys intact, plus the README, so you can load it and check it fits before you pay.
How was it checked?
117 of 117 checks pass, re-run on the delivered files by an independent script with plain pandas: every primary key is unique and every foreign key resolves; the 4-5-4 calendar: 52 Sunday-to-Saturday weeks a year, 4-5-4 periods, closures on Thanksgiving and Christmas Day; both slowly changing dimensions chain without gaps or overlaps with one current version, and every fact carries the version in force at its time; prices: every list and shelf price sits on a price point (x.x9 under $10, .49 or .99 above), every price change moves the way its reason says; a price cut or clearance lands on the next price point down, a multi-buy or buy one get one free sells a unit for its share of the deal, to the cent; the shelf: close = open + received - sold - waste on every row, tomorrow opens with today's close, demand = sold + lost, a sale is lost only when the shelf ran empty, only fresh food spoils; demand is the product of its delivered factors and unbiased around it; the effects above are measured, not assumed; baskets: totals are the sums of their lines, lines ring up exactly the units the shelf sold, at that day's price and promotion; cards only after the member joined; returns after their sale, of what was bought, at what was paid.
Is there a project to do with it?
Yes. The zip has a project kit: a brief from the Commercial Director of a ten-store grocery chain (Two-year trading review for the board (fiscal 2024 and 2025)), 18 questions from warm-up to stretch, an answer key with the SQL behind every answer, a checker (kit/check.py) that grades your answers and names the common misreading a wrong one matches, and a write-up template. Every answer was worked out twice on these files, in DuckDB SQL and separately in pandas.
Can I use it commercially?
Yes: in any project, course, benchmark, demo or product, commercial or not. You may not resell or redistribute the dataset itself as a dataset.
What should I know before using it?
Tracked range: 240 products stand for the chain's assortment, so a basket holds only the lines from that range (median 2); a real basket would have more lines of untracked products. Fiscal years are named for the calendar year they start in (fiscal 2024 runs 4 February 2024 to 1 February 2025). Money is in US dollars. true_elasticity, baseline_units and the demand factors are ground truth no retailer could see; use sales and prices alone for a realistic exercise, and the truth to score it. Stores, brands, products, suppliers and members are invented; no real retailer, brand or person is represented.
Can I get a bigger or different version?
Yes. RECIPE.json runs in Misata Studio or through Misata's MCP server to make a variant, or ask us to build one to your spec.

Want it bigger, in another setting, or with your own columns? Have us build it or make it in Studio.

More premium datasets

All 9
  • Card spending each month, debit and credit

    2,500 US households at five banks and seven card issuers, two years

    Five banks' raw statement lines, with every label behind them

    Answer key

    • description
    • merchant_name
    • category
    • +6 more
    Rows
    7,627,186
    Tables
    14
    Checks
    50 of 50
    Free previewProfile and preview
  • transactions, rows per monthMay 2025 – Oct 2026

    One UAE bank, 10,000 customers, eighteen months

    A UAE bank's whole trail from KYC to STR, with the laundering nobody caught

    Answer key

    • is_suspicious
    • typology
    • ring_id
    • +7 more
    Rows
    11,711,393
    Tables
    16
    Checks
    129 of 129
    Free previewProfile and preview
  • An X-bar chart with the truth underneath: flatness, characteristic 139

    3 plants, 36 CNC machines, six months

    Six months of control charts with the ground truth underneath

    Answer key

    • active_cause_id
    • true_cause_shift_sigma
    • true_tool_wear_sigma
    • +6 more
    Rows
    515,442
    Tables
    15
    Checks
    101 of 101
    Free preview18-question projectProfile and preview