-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathschema.sql
More file actions
135 lines (118 loc) · 5.24 KB
/
Copy pathschema.sql
File metadata and controls
135 lines (118 loc) · 5.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
-- ============================================================================
-- sql-for-agents — the example schema (SQLite)
-- Synthetic data only. No customer data, no production anything.
-- 9 tables · 8 foreign keys · 1 many-to-many junction · 1 view
-- ============================================================================
PRAGMA foreign_keys = ON;
-- ---------------------------------------------------------------- roots ----
-- Three tables that reference nobody. Everything else hangs off these.
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
full_name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
city TEXT NOT NULL,
segment TEXT NOT NULL CHECK (segment IN ('consumer','business','enterprise')),
joined_on TEXT NOT NULL
);
CREATE TABLE plans (
id INTEGER PRIMARY KEY,
code TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
monthly_fee REAL NOT NULL CHECK (monthly_fee >= 0),
data_gb INTEGER NOT NULL,
minutes INTEGER NOT NULL,
is_active INTEGER NOT NULL DEFAULT 1
);
CREATE TABLE addons (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
monthly_fee REAL NOT NULL CHECK (monthly_fee >= 0)
);
-- ------------------------------------------------------------ the spine ----
-- A subscription is the join between a person and a price list.
CREATE TABLE subscriptions (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
plan_id INTEGER NOT NULL REFERENCES plans(id) ON DELETE RESTRICT,
msisdn TEXT NOT NULL UNIQUE,
started_on TEXT NOT NULL,
ended_on TEXT,
status TEXT NOT NULL CHECK (status IN ('active','suspended','closed'))
);
CREATE TABLE tickets (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
opened_at TEXT NOT NULL,
topic TEXT NOT NULL,
severity INTEGER NOT NULL CHECK (severity BETWEEN 1 AND 4),
resolved_at TEXT
);
-- ---------------------------------------------------------- the leaves -----
-- Many-to-many: one subscription can carry several add-ons, one add-on sits on
-- many subscriptions. Composite primary key, two foreign keys, no surrogate id.
CREATE TABLE subscription_addons (
subscription_id INTEGER NOT NULL REFERENCES subscriptions(id) ON DELETE CASCADE,
addon_id INTEGER NOT NULL REFERENCES addons(id) ON DELETE CASCADE,
added_on TEXT NOT NULL,
PRIMARY KEY (subscription_id, addon_id)
);
CREATE TABLE devices (
id INTEGER PRIMARY KEY,
subscription_id INTEGER NOT NULL REFERENCES subscriptions(id) ON DELETE CASCADE,
model TEXT NOT NULL,
imei TEXT NOT NULL UNIQUE,
instalments_left INTEGER NOT NULL DEFAULT 0 CHECK (instalments_left >= 0)
);
CREATE TABLE usage_daily (
id INTEGER PRIMARY KEY,
subscription_id INTEGER NOT NULL REFERENCES subscriptions(id) ON DELETE CASCADE,
day TEXT NOT NULL,
data_mb INTEGER NOT NULL CHECK (data_mb >= 0),
minutes INTEGER NOT NULL CHECK (minutes >= 0),
sms INTEGER NOT NULL CHECK (sms >= 0),
UNIQUE (subscription_id, day)
);
CREATE TABLE invoices (
id INTEGER PRIMARY KEY,
subscription_id INTEGER NOT NULL REFERENCES subscriptions(id) ON DELETE CASCADE,
period TEXT NOT NULL, -- 'YYYY-MM'
amount REAL NOT NULL CHECK (amount >= 0),
status TEXT NOT NULL CHECK (status IN ('paid','open','overdue')),
paid_on TEXT,
UNIQUE (subscription_id, period)
);
-- ------------------------------------------------------------- indexes -----
-- SQLite indexes a PRIMARY KEY and a UNIQUE for you. It does NOT index the
-- child side of a foreign key — and that is the classic slow-join surprise.
CREATE INDEX idx_sub_customer ON subscriptions (customer_id);
CREATE INDEX idx_sub_plan ON subscriptions (plan_id);
CREATE INDEX idx_dev_sub ON devices (subscription_id);
CREATE INDEX idx_usage_sub_day ON usage_daily (subscription_id, day);
CREATE INDEX idx_inv_sub ON invoices (subscription_id);
CREATE INDEX idx_tickets_cust ON tickets (customer_id);
CREATE INDEX idx_subadd_addon ON subscription_addons (addon_id);
-- ---------------------------------------------------------------- view -----
-- Hand an agent a VIEW, not the base tables: the join is already decided,
-- the columns are already chosen, and the shape cannot be got wrong.
CREATE VIEW v_monthly_bill AS
SELECT
c.id AS customer_id,
c.full_name,
c.city,
s.msisdn,
p.name AS plan,
i.period,
i.amount,
i.status,
COALESCE(a.addon_fees, 0) AS addon_fees,
ROUND(i.amount + COALESCE(a.addon_fees, 0), 2) AS total_due
FROM invoices i
JOIN subscriptions s ON s.id = i.subscription_id
JOIN customers c ON c.id = s.customer_id
JOIN plans p ON p.id = s.plan_id
LEFT JOIN (
SELECT sa.subscription_id, SUM(ad.monthly_fee) AS addon_fees
FROM subscription_addons sa
JOIN addons ad ON ad.id = sa.addon_id
GROUP BY sa.subscription_id
) a ON a.subscription_id = s.id;