Scene 01
What we'll learn in this lesson
Welcome to this optional exercise. We will reuse group by to find the customer who spent the most on each product. Then we will turn that calculated ID into a relationship. You can skip this exercise and continue with writes and transactions; no later lesson needs this formula.
Scene 02
Choose a buyer by total spending
Why do we group before sorting? Imagine Ada buys this product twice, spending six each time, while Grace spends ten once. The largest single purchase belongs to Grace. But Ada's combined twelve makes her the best buyer. Group by combines her purchases before order by and limit choose a winner. We use the grouping from lesson six to choose the winning customer.
Example & screen notes
Best buyer = most spent across all purchases
Illustrative example:
Ada: 6 + 6 = 12
Grace: 10 = 10
ORDER BY one row + LIMIT 1 → Grace (10)
GROUP BY buyer + SUM + ORDER BY + LIMIT 1 → Ada (12)
Group first, then sort and keep one customer
Scene 03
Calculate each product's best buyer
Let's find each product's best buyer. Best means the customer who spent the most on this product across all their invoices. Group its rows by customer, sum each customer's spending, then sort and keep one ID. Sorting individual rows would find the largest single purchase instead. If totals are equal, the lower customer ID wins.
Example & screen notes
>>>
>>> ######## Find each product's best buyer ########
>>>
>>> best_customer = product.formulaColumn(
... "best_customer_id", dtype="I",
... select=dict(
... table="shop.invoice_row",
... columns="@invoice_id.customer_id",
... where="$product_code = #THIS.code",
... group_by="@invoice_id.customer_id",
... order_by="SUM($calculated_total) DESC, "
... "@invoice_id.customer_id",
... limit=1,
... ),
... )
Scene 04
Follow a relation from the calculated ID
Let's connect that ID to the customer. A weak relation starts from the calculated column. It creates no stored foreign key. Reload the model and refresh the table handles. Let's read the buyer's name and city. The path starts from our formula, just like a stored ID. Linus is the best buyer of biscuits and black tea; Grace is the best buyer of bottles. With no sales, the ID and customer fields are null. True confirms that no migration is needed.
Example & screen notes
>>>
>>> ######## Connect that ID to the customer ########
>>>
>>> _ = best_customer.weak_relation("shop.customer.id")
>>> db.reloadModel(model)
>>> products = db.table("shop.product")
>>> rows = db.table("shop.invoice_row")
>>>
>>> ######## Read the buyer's name and city ########
>>>
>>> products.query(
... columns="$code, $best_customer_id, "
... "@best_customer_id.name, @best_customer_id.city",
... order_by="$code", limit=3,
... ).fetch()
[[code=BIS,best_customer_id=3,_best_customer_id_name=Linus,_best_customer_id_city=Helsinki], [code=BOT,best_customer_id=2,_best_customer_id_name=Grace,_best_customer_id_city=London], [code=BTEA,best_customer_id=3,_best_customer_id_name=Linus,_best_customer_id_city=Helsinki]]
>>> db.commit()
>>> db.migration_plan().empty
True
Scene 05
Thank you — next steps
Thanks for following along! We combined purchases, selected one winning customer, and followed a weak relation from a calculated ID. This optional extension adds no stored columns. Continue with writes and transactions; that lesson also works without this exercise.