OriginChainDB
SQL

SQL models by industry

These examples use SQL for orders, account entries, inventory and events. Each shows a table design and a useful question to ask it. Create the example tables before running the queries; they are separate from the quickstart product table.

Build a report from related business recordsThe industry tables are separate fixtures; create and validate them before running these queries.
  1. Model the business recordsFor example, orders, order lines and products.
  2. Connect their keysMake the relationships needed by the report explicit.
  3. Select the reporting periodUse the intended time boundaries and business conditions.
  4. Join and calculate totalsCompare the grouped result with a small known sample.

Retail: an order book

Orders, their lines, and the products they point at. The reporting questions are all aggregates over a join.

The tables

shop.products (id PK, name, brand, price)
shop.orders   (id PK, customer_id, status, placed_at)
shop.lines    (id PK, order_id -> orders.id, product_id -> products.id, qty)

Revenue by brand, last month

One statement, joined and grouped in the engine, rather than three round trips and a loop in your service.

SELECT p.brand,
       SUM(l.qty * p.price) AS revenue,
       COUNT(DISTINCT o.id) AS orders
FROM shop.lines l
JOIN shop.orders   o ON l.order_id = o.id
JOIN shop.products p ON l.product_id = p.id
WHERE o.status = 'paid'
GROUP BY p.brand
ORDER BY revenue DESC

The same catalog, searched

Add the relevant search index before searching this catalog. Attached table indexes and explicit document/vector writes have different update paths. A SQL INSERT does not automatically generate an embedding. See writing and indexing.

SELECT id, name FROM shop.products WHERE brand = 'Aero' ORDER BY price ASC

Financial services: a ledger

Append-only entries, balances derived rather than stored, and money kept in minor units so totals never drift.

The tables

fin.accounts (id PK, holder, currency, opened_at)
fin.entries  (id PK, account_id -> accounts.id, amount_minor, kind, booked_at)

Balance per account

Derived from the entries, so it cannot disagree with them.

SELECT a.id, a.holder, a.currency,
       SUM(e.amount_minor) AS balance_minor
FROM fin.accounts a
JOIN fin.entries  e ON e.account_id = a.id
GROUP BY a.id, a.holder, a.currency

who sees which rows

Run the query with the intended database identity and verify its table grants, row policies and column masks. A WHERE clause is not a substitute for database authorization. Test both permitted and denied records.

Logistics: inventory with reservations

Stock on hand, minus what is promised. The trap is reading availability and acting on it after someone else already did.

The tables

wh.stock        (sku PK, on_hand)
wh.reservations (id PK, sku -> stock.sku, qty, state, made_at)

What is actually available

Subtract live reservations from stock in one statement.

SELECT s.sku,
       s.on_hand,
       COALESCE(SUM(r.qty), 0) AS reserved,
       s.on_hand - COALESCE(SUM(r.qty), 0) AS available
FROM wh.stock s
LEFT JOIN wh.reservations r
       ON r.sku = s.sku AND r.state = 'held'
GROUP BY s.sku, s.on_hand

make the decision atomic

Reading availability and then reserving is two steps, and another writer fits between them. Do the read and the write in one transaction — see transactions — so the reservation either wins or fails cleanly.

Product analytics: an event stream

High write volume, and questions that are almost always time-bucketed.

The tables

app.events (id PK, user_id, name, props_json, at)

Daily active users

Grouped in the engine over the whole table, not sampled in a job.

SELECT DATE_TRUNC('day', at) AS day,
       COUNT(DISTINCT user_id) AS dau
FROM app.events
WHERE at >= '2026-09-01T00:00:00Z'
GROUP BY day
ORDER BY day

when a query becomes a dashboard

If a dashboard repeatedly runs an expensive summary, consider saving it as a materialized view. That saved result needs an explicit refresh. See materialized views.

What these have in common

  • The tables are the design. Every cheap question later comes from a key or a foreign key you declared now.

  • Prepare each query method. Reuse source IDs across rows and search indexes, and keep each required representation current.

  • Policy is enforced below your query. Row-level security and masking apply to the statement itself, including inside aggregates.

  • Start from zero — Schema, insert, select, join, aggregate.

  • More examples — Single-purpose recipes with their responses.

© 2026 Silicoyn Technologies Pvt Ltd

On this page