Initial schema
Three tables to start. Customer holds an identifier and a name. Invoice records which customer placed it, where, and when. InvoiceLine adds one row per item on each invoice.
Building a relational invoicing schema step by step — tables, foreign keys, migrations, and a trigger.
Designing a relational schema for an invoicing system and then evolving it. The migrations add a product catalogue, customer addresses, deliveries and a premium-customer flag. Each section below follows a step from the original exercise.
The exercise step by step. The SQL is hidden by default. Use the toggle on each step to view the statements.
Three tables to start. Customer holds an identifier and a name. Invoice records which customer placed it, where, and when. InvoiceLine adds one row per item on each invoice.
Two customers, three invoices and six line items, enough to exercise the schema.
The schema has two weaknesses. First, there is no current customer address, since the city can only be inferred from a customer's last invoice and that may be stale for infrequent buyers. Second, there is no product catalogue, so item prices live on InvoiceLine and the current price has to be inferred from the last sale, which is expensive and wrong for rarely-sold items.
Rename the loose Item column to a proper foreign key ProductId. A new Product table now holds the description, price and weight for each item.
Add a City column to Customer, backfill it from the most recent invoice and then make it NOT NULL. A new Delivery table tracks shipment status, and each Invoice now links to its delivery.
Mark a customer as Premium once they have more than two invoices. An AFTER INSERT trigger on Invoice recounts and updates the flag on every new insert.
Insert a third invoice for customer 1, and the trigger promotes them to Premium.
The final schema is loaded with the seed data already inserted. Write your own query or pick one from the samples below, then use Cmd/Ctrl + Enter to run.
A few queries that exercise the final schema with joins across all five tables.
SELECT Name, City, Premium FROM Customer;
SELECT
Invoice.Id,
Customer.Name,
Invoice.Date,
Delivery.Status
FROM
Invoice
INNER JOIN Customer ON Invoice.CustomerId = Customer.Id
INNER JOIN Delivery ON Invoice.DeliveryId = Delivery.Id
ORDER BY
Invoice.Date;
SELECT
Invoice.Id AS Invoice,
Customer.Name,
ROUND(SUM(InvoiceLine.Quantity * InvoiceLine.Price), 2) AS Total
FROM
Invoice
INNER JOIN Customer ON Invoice.CustomerId = Customer.Id
INNER JOIN InvoiceLine ON InvoiceLine.InvoiceId = Invoice.Id
GROUP BY
Invoice.Id
ORDER BY
Total DESC;
SELECT
Product.Description,
SUM(InvoiceLine.Quantity) AS Sold,
ROUND(SUM(InvoiceLine.Quantity * InvoiceLine.Price), 2) AS Revenue
FROM
InvoiceLine
INNER JOIN Product ON InvoiceLine.ProductId = Product.Id
GROUP BY
Product.Id
ORDER BY
Sold DESC;