Skip to content
BeispielWerk
Deutsch

Sample e-commerce database: 8 related tables as JSON, CSV and PostgreSQL

A fictional US-style online shop in 8 related tables: customers, orders, order items, payments and shipments, with prices in USD. Download it as JSON, CSV or a PostgreSQL script, in a small and a medium size. Amounts reconcile, every reference is valid, and the files never change.

Complete files: schema, seed scripts and everything in one JSON

  • Sample shop: schema for PostgreSQL (CREATE TABLE)Fictional

    -- Sample shop (US flavour, USD): schema for PostgreSQL. All data is invented; money is stored in cents.
    -- License: CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/)
     …

    Eight tables with primary and foreign keys and CHECK rules for tax rates, status and totals. Also loads in SQLite.

  • Sample shop (small): tables, columns and relationsFictional

    Sample shop (small, US flavour): 8 tables, money in cents (USD)
    
    Relations …

    Overview of the eight tables with row counts, columns and the rules of the data (12 customers, 24 orders).

  • Sample shop (small): schema and data as SQL (PostgreSQL)Fictional

    -- Sample shop (small, US flavour, USD): schema and data for PostgreSQL. All data is invented.
    -- License: CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/)
    BEGIN; …

    One transaction with CREATE TABLE and INSERT for every table, in foreign-key order. Amounts in cents (USD).

  • Sample shop (small): all tables in one JSON fileFictional

    {
      "categories": [
        { …

    One object with an array per table, suited for seed scripts. Amounts in cents (USD), references valid.

  • Sample shop (medium): tables, columns and relationsFictional

    Sample shop (medium, US flavour): 8 tables, money in cents (USD)
    
    Relations …

    Overview of the eight tables with row counts, columns and the rules of the data (60 customers, 160 orders).

  • Sample shop (medium): schema and data as SQL (PostgreSQL)Fictional

    -- Sample shop (medium, US flavour, USD): schema and data for PostgreSQL. All data is invented.
    -- License: CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/)
    BEGIN; …

    One transaction with CREATE TABLE and INSERT for every table, in foreign-key order. Amounts in cents (USD).

  • Sample shop (medium): all tables in one JSON fileFictional

    {
      "categories": [
        { …

    One object with an array per table, suited for seed scripts. Amounts in cents (USD), references valid.

Single tables as JSON and CSV (small and medium)

  • Sample shop (small): categories as JSONFictional

    [
      {
        "id": 1, …

    Invented shop (USD): amounts in cents, totals match the line items, references between the tables are valid.

  • Sample shop (small): categories as CSVFictional

    id,name,slug
    1,Office & Stationery,office-stationery
    2,Household,household …

    UTF-8, comma separated, empty cell = NULL. Amounts in cents (USD), references between the tables valid.

  • Sample shop (small): products as JSONFictional

    [
      {
        "id": "prd_001", …

    Invented shop (USD): amounts in cents, totals match the line items, references between the tables are valid.

  • Sample shop (small): products as CSVFictional

    id,sku,name,category_id,price_net_cents,tax_rate,stock,active,created_at
    prd_001,SKU-0001,"Notebook A5, squared",1,420,7,109,true,2025-10-04T08:00:00Z
    prd_002,SKU-0002,"Ballpoint pens, 10 pack",1,599,7,49,true,2025-07-14T08:00:00Z …

    UTF-8, comma separated, empty cell = NULL. Amounts in cents (USD), references between the tables valid.

  • Sample shop (small): customers as JSONFictional

    [
      {
        "id": "cus_0001", …

    Invented shop (USD): amounts in cents, totals match the line items, references between the tables are valid.

  • Sample shop (small): customers as CSVFictional

    id,customer_number,salutation,first_name,last_name,email,phone,newsletter,created_at
    cus_0001,C-100001,ms,Emily,Lee,emily.lee.1@example.net,+1 310 555 0126,false,2026-03-06T08:00:00Z
    cus_0002,C-100002,ms,Ava,Davis,ava.davis.2@example.net,+1 …

    UTF-8, comma separated, empty cell = NULL. Amounts in cents (USD), references between the tables valid.

  • Sample shop (small): addresses as JSONFictional

    [
      {
        "id": "adr_0001", …

    Invented shop (USD): amounts in cents, totals match the line items, references between the tables are valid.

  • Sample shop (small): addresses as CSVFictional

    id,customer_id,type,street,house_number,postal_code,city,region,country
    adr_0001,cus_0001,billing,Prototype Place,8782,63248,Testburg,CA,US
    adr_0002,cus_0002,billing,Fixture Boulevard,8018,48426,Mockford,CO,US …

    UTF-8, comma separated, empty cell = NULL. Amounts in cents (USD), references between the tables valid.

  • Sample shop (small): orders as JSONFictional

    [
      {
        "id": "ord_00001", …

    Invented shop (USD): amounts in cents, totals match the line items, references between the tables are valid.

  • Sample shop (small): orders as CSVFictional

    id,order_number,customer_id,billing_address_id,shipping_address_id,status,ordered_at,shipping_cents,total_net_cents,total_tax_cents,total_gross_cents,currency
    ord_00001,O-2026-000001,cus_0010,adr_0013,adr_0013,delivered,2026-01-16T12:31:45Z …

    UTF-8, comma separated, empty cell = NULL. Amounts in cents (USD), references between the tables valid.

  • Sample shop (small): order_items as JSONFictional

    [
      {
        "id": "itm_000001", …

    Invented shop (USD): amounts in cents, totals match the line items, references between the tables are valid.

  • Sample shop (small): order_items as CSVFictional

    id,order_id,product_id,quantity,unit_price_net_cents,tax_rate,net_cents,tax_cents,gross_cents
    itm_000001,ord_00001,prd_024,1,1495,7,1495,105,1600
    itm_000002,ord_00002,prd_017,1,1290,7,1290,90,1380 …

    UTF-8, comma separated, empty cell = NULL. Amounts in cents (USD), references between the tables valid.

  • Sample shop (small): payments as JSONFictional

    [
      {
        "id": "pay_00001", …

    Invented shop (USD): amounts in cents, totals match the line items, references between the tables are valid.

  • Sample shop (small): payments as CSVFictional

    id,order_id,method,status,amount_cents,card_last4,paid_at,refunded_at
    pay_00001,ord_00001,prepayment,paid,2199,,2026-01-17T12:31:45Z,
    pay_00002,ord_00002,prepayment,paid,3579,,2026-01-27T21:28:09Z, …

    UTF-8, comma separated, empty cell = NULL. Amounts in cents (USD), references between the tables valid.

  • Sample shop (small): shipments as JSONFictional

    [
      {
        "id": "shp_00001", …

    Invented shop (USD): amounts in cents, totals match the line items, references between the tables are valid.

  • Sample shop (small): shipments as CSVFictional

    id,order_id,carrier,tracking_number,shipped_at,delivered_at
    shp_00001,ord_00001,ExampleParcel,TEST-0001-3GB,2026-01-18T04:31:45Z,2026-01-21T04:31:45Z
    shp_00002,ord_00002,SampleLogistics,TEST-0002-17M,2026-01-28T01:28:09Z,2026-02-01T01:28:09 …

    UTF-8, comma separated, empty cell = NULL. Amounts in cents (USD), references between the tables valid.

  • Sample shop (medium): categories as JSONFictional

    [
      {
        "id": 1, …

    Invented shop (USD): amounts in cents, totals match the line items, references between the tables are valid.

  • Sample shop (medium): categories as CSVFictional

    id,name,slug
    1,Office & Stationery,office-stationery
    2,Household,household …

    UTF-8, comma separated, empty cell = NULL. Amounts in cents (USD), references between the tables valid.

  • Sample shop (medium): products as JSONFictional

    [
      {
        "id": "prd_001", …

    Invented shop (USD): amounts in cents, totals match the line items, references between the tables are valid.

  • Sample shop (medium): products as CSVFictional

    id,sku,name,category_id,price_net_cents,tax_rate,stock,active,created_at
    prd_001,SKU-0001,"Notebook A5, squared",1,420,7,18,true,2025-07-13T08:00:00Z
    prd_002,SKU-0002,"Ballpoint pens, 10 pack",1,599,7,49,true,2025-11-02T08:00:00Z …

    UTF-8, comma separated, empty cell = NULL. Amounts in cents (USD), references between the tables valid.

  • Sample shop (medium): customers as JSONFictional

    [
      {
        "id": "cus_0001", …

    Invented shop (USD): amounts in cents, totals match the line items, references between the tables are valid.

  • Sample shop (medium): customers as CSVFictional

    id,customer_number,salutation,first_name,last_name,email,phone,newsletter,created_at
    cus_0001,C-100001,ms,Chloe,Carter,chloe.carter.1@example.net,+1 310 555 0179,false,2026-03-31T13:47:35Z
    cus_0002,C-100002,ms,Amelia,Taylor,amelia.taylor.2@ …

    UTF-8, comma separated, empty cell = NULL. Amounts in cents (USD), references between the tables valid.

  • Sample shop (medium): addresses as JSONFictional

    [
      {
        "id": "adr_0001", …

    Invented shop (USD): amounts in cents, totals match the line items, references between the tables are valid.

  • Sample shop (medium): addresses as CSVFictional

    id,customer_id,type,street,house_number,postal_code,city,region,country
    adr_0001,cus_0001,billing,Sandbox Drive,9605,13706,Demoville,CO,US
    adr_0002,cus_0002,billing,Test Road,8802,62425,Mockford,OR,US …

    UTF-8, comma separated, empty cell = NULL. Amounts in cents (USD), references between the tables valid.

  • Sample shop (medium): orders as JSONFictional

    [
      {
        "id": "ord_00001", …

    Invented shop (USD): amounts in cents, totals match the line items, references between the tables are valid.

  • Sample shop (medium): orders as CSVFictional

    id,order_number,customer_id,billing_address_id,shipping_address_id,status,ordered_at,shipping_cents,total_net_cents,total_tax_cents,total_gross_cents,currency
    ord_00001,O-2026-000001,cus_0016,adr_0021,adr_0021,delivered,2026-01-05T23:59:52Z …

    UTF-8, comma separated, empty cell = NULL. Amounts in cents (USD), references between the tables valid.

  • Sample shop (medium): order_items as JSONFictional

    [
      {
        "id": "itm_000001", …

    Invented shop (USD): amounts in cents, totals match the line items, references between the tables are valid.

  • Sample shop (medium): order_items as CSVFictional

    id,order_id,product_id,quantity,unit_price_net_cents,tax_rate,net_cents,tax_cents,gross_cents
    itm_000001,ord_00001,prd_027,2,1190,0,2380,0,2380
    itm_000002,ord_00001,prd_017,1,1290,7,1290,90,1380 …

    UTF-8, comma separated, empty cell = NULL. Amounts in cents (USD), references between the tables valid.

  • Sample shop (medium): payments as JSONFictional

    [
      {
        "id": "pay_00001", …

    Invented shop (USD): amounts in cents, totals match the line items, references between the tables are valid.

  • Sample shop (medium): payments as CSVFictional

    id,order_id,method,status,amount_cents,card_last4,paid_at,refunded_at
    pay_00001,ord_00001,debit_card,paid,4359,6748,2026-01-06T00:04:52Z,
    pay_00002,ord_00002,paypal,failed,6029,,, …

    UTF-8, comma separated, empty cell = NULL. Amounts in cents (USD), references between the tables valid.

  • Sample shop (medium): shipments as JSONFictional

    [
      {
        "id": "shp_00001", …

    Invented shop (USD): amounts in cents, totals match the line items, references between the tables are valid.

  • Sample shop (medium): shipments as CSVFictional

    id,order_id,carrier,tracking_number,shipped_at,delivered_at
    shp_00001,ord_00001,SampleLogistics,TEST-0001-1XG,2026-01-07T08:59:52Z,2026-01-10T08:59:52Z
    shp_00002,ord_00004,ExampleParcel,TEST-0004-6MW,2026-01-13T09:24:19Z,2026-01-14T09:24:19 …

    UTF-8, comma separated, empty cell = NULL. Amounts in cents (USD), references between the tables valid.

What is in the data

Two sizes of the same shop. The small set (12 customers, 24 orders) is meant for reading and unit tests, the medium set (60 customers, 160 orders) for dashboards, pagination and joins. They are separate datasets, not prefixes of each other. In the files, net means before tax and gross means after tax.

Tables with row counts and purpose
TableSmallMediumPurpose and rules
categories66Product groups. Food is taxed at 0 %, everything else at 7 %.
products2929Prices are net (before tax), in cents. Some products are sold out (stock = 0) or inactive (active = false).
customers1260Invented people, e-mail only on example.* domains. phone can be NULL. The last customers never ordered.
addresses1680Every customer has a billing address, some also a separate shipping address. Street, city and state are invented and unrelated to the random ZIP code.
orders24160Totals = sum of the order items + shipping. shipping_cents is 599, or 0 from 75.00 USD of goods. Shipping is untaxed.
order_items47327Price and tax rate are the values at order time. gross = net + tax, net = quantity * unit price. Sales tax is added on top of the price: 0 % on food, 7 % on everything else. This is a fictional rule that does not follow any real state.
payments24152At most one payment per order. Cancelled orders without an instant payment method have none. Invoices can be open although the goods have shipped. card_last4 holds the last four digits of a card payment, nothing more.
shipments15137Only for shipped, delivered and refunded orders. delivered_at is NULL while the parcel is on its way.

How the tables relate

categories 1 ── n products 1 ── n order_items n ── 1 orders
customers  1 ── n addresses                          │
customers  1 ── n orders ─────────────────────────────┤
orders     1 ── 0..1 payments                         │
orders     1 ── 0..1 shipments ───────────────────────┘
orders.billing_address_id, orders.shipping_address_id -> addresses.id

Rules the data follows

  • Money in cents. A line item has net = quantity × unit price, tax = round(net × rate / 100) and gross = net + tax.
  • Tax and shipping. Sales tax is added on top of the price: 0 % on food, 7 % on everything else. This is a fictional rule that does not follow any real state. Shipping is 5.99 USD and untaxed, free from 75.00 USD of goods.
  • Order totals are the sums of the line items plus shipping, and total gross = total net + total tax. A payment amount always equals the order total.
  • Timelines make sense. Ordered, then paid, then shipped, then delivered. Invoice payments may arrive after shipping, and a refund comes after delivery. Everything lies before the fixed “today”.
  • References are valid. Every foreign key points to an existing row, and an order's addresses belong to the order's customer.
  • Edge cases are built in: sold-out and inactive products, a customer without a phone number, customers who never ordered, cancelled orders with and without a failed payment, refunds, and shipped orders whose invoice is still pending.

These rules are checked by automated tests, including a load of the generated SQL into a real database with foreign keys switched on.

Load it into PostgreSQL

Download the seed script of the size you want (it creates the tables and inserts the rows inside one transaction), then run:

createdb shop_demo
psql shop_demo -f shop-us-small-seed.sql

If you already have your own schema, use the schema file as a reference and load the per-table CSV files with \copy, or insert the JSON from a script:

import { readFileSync } from 'node:fs'
const shop = JSON.parse(readFileSync('shop-us-small.json', 'utf8'))
for (const order of shop.orders) { /* insert with your ORM */ }

Example queries

These run unchanged on PostgreSQL and SQLite and return rows on the medium set.

Revenue and order count per status

SELECT status, COUNT(*) AS orders, SUM(total_gross_cents) / 100.0 AS revenue
FROM orders
GROUP BY status
ORDER BY revenue DESC;

Top products by net revenue

SELECT p.name, SUM(i.quantity) AS units, SUM(i.net_cents) / 100.0 AS net_revenue
FROM order_items i
JOIN products p ON p.id = i.product_id
GROUP BY p.id, p.name
ORDER BY net_revenue DESC
LIMIT 5;

Shipped but not yet paid (open invoices)

SELECT o.order_number, c.email, o.total_gross_cents / 100.0 AS amount, o.ordered_at
FROM orders o
JOIN customers c ON c.id = o.customer_id
JOIN payments pay ON pay.order_id = o.id
WHERE pay.status = 'pending' AND o.status IN ('shipped', 'delivered')
ORDER BY o.ordered_at;

Customers who never ordered

SELECT c.customer_number, c.email
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.id IS NULL;

Tax collected per tax rate

SELECT tax_rate, SUM(net_cents) / 100.0 AS net, SUM(tax_cents) / 100.0 AS tax
FROM order_items
GROUP BY tax_rate
ORDER BY tax_rate;

License

The files on this page are generated from this site's own code and word lists and are released under CC0 1.0 Universal, a public domain dedication. You may copy, modify and use these files for any purpose, including commercial projects, without asking and without attribution. The files are provided as they are, without any warranty. All data is invented and must not be used to represent real people, accounts or transactions. The license covers these generated files only, not the text of this site.

Related

The test data edge cases page has deliberately broken versions of one order from this shop, one violated rule each. The German flavour of the same shop (EUR, VAT, SEPA payments with test IBANs created like those of the test IBAN generator) is in the German library: Beispiel-Shop-Datenbank (in German). Order numbers follow the idea behind the invoice number generator.

Frequently asked questions

Is any of this real data?
No. Names are combined from common first and last names and addresses use invented streets and cities, so a real person may share a name, but nothing else belongs to anyone. E-mail addresses use the reserved example.* domains, phone numbers come from the 555-01xx range that is reserved for fiction, and ZIP codes are random numbers that do not belong to the invented city.
Why are amounts in cents?
Integers avoid rounding errors, which is how most payment systems store money. Divide by 100 for display. Tax is rounded per line item, and the order total is the sum of its lines plus shipping.
Which sales tax rules does the data follow?
A deliberately simple, fictional rule that does not follow any real state: prices are net, tax is added on top, food is taxed at 0 % and everything else at 7 %. Shipping is 5.99 USD, untaxed, and free from 75.00 USD of goods. The rule is written into the CHECK constraints so your code can rely on it.
Is there a version with other currencies or VAT?
Yes, a German flavour exists (EUR, VAT of 19 % and 7 % included in the price, SEPA payments with test IBANs). It lives in the German library, see the link at the end of this page. The schema is the same apart from one payment column.
Can I load it into MySQL, SQLite or another database?
The SQL script is written for PostgreSQL. It also loads unchanged into SQLite; our tests do exactly that with foreign keys enforced. For MySQL, replace timestamptz with datetime and check the CHECK constraints for your version. The JSON and CSV files work with any database.
Does the data change over time?
No. The same size always produces the same files, and all dates are relative to a fixed “today” of 24 October 2026. If the data model changes, it will be announced here.
Are there edge cases in the data?
Yes, on purpose: sold-out and inactive products, customers without a phone number or without orders, cancelled orders without a payment, and shipped orders whose invoice is still unpaid. They are listed under “What is in the data”. Deliberately broken files are kept apart on the page about test data edge cases.
Can I use the files in my own project?
Yes. You may copy, modify and use these files for any purpose, including commercial projects, without asking and without attribution. The license is CC0 1.0 Universal. The files are provided as they are, without any warranty. All data is invented and must not be used to represent real people, accounts or transactions.

Sources and status

Content last reviewed: October 2026. Not legal or tax advice; the statements of the competent authorities prevail.