What Does One Row Actually Describe?
Botie has an asset list and a stack of inspection notes. Decide what belongs on each row before asking the database a question.
12 sections · 4 questions · 1 coding challenge
The arrow keys move between sections too.
Queries Should Survive a Change in Storage
BOTIE
Our maintenance tables use that separation: an asset ID identifies a pump without telling us which disk page holds it. The invented records illustrate the idea; SQL tables also permit duplicates unless constraints or queries remove them.
Sources for this history
The Same Pump Appears Three Times
The maintenance team gives Botie a spreadsheet. P1 appears on several lines because it has been inspected several times. Does that mean the team owns several pumps called P1, or that one pump has a history?
We need an answer before counting anything. A table becomes useful when each row has a defined meaning and the fields describe that same kind of thing. We will separate the asset from the visits made to it, then give each its own identity.
These maintenance records are invented. The workshop exposes record operations; the SQL worksheet runs actual SQLite queries.
Step 1 of 2
One asset can have several visits
The asset register and inspection log describe different units.
| Table | One row means | Identity |
|---|---|---|
| assets | One maintained asset | asset_id |
| inspections | One inspection visit | inspection_id |
| readings | One recorded measurement | reading_id |
assets
- One row means
- One maintained asset
- Identity
- asset_id
inspections
- One row means
- One inspection visit
- Identity
- inspection_id
readings
- One row means
- One recorded measurement
- Identity
- reading_id
Step 2 of 2
A description is not an identifier
Two objects can both be pumps.
| asset_id | kind | site |
|---|---|---|
| P1 | pump | north |
| P4 | pump | south |
P1
- kind
- pump
- site
- north
P4
- kind
- pump
- site
- south
Follow the same records into the query
Identity is retained while unrelated columns are left out.
Keep the identity beside the description
Choose the asset ID and kind, then order the result by ID. Several pumps may share a kind without becoming the same asset.
BOTIE
Asset identities and kinds
[
{
"asset_id": "P1",
"kind": "pump"
},
{
"asset_id": "P2",
"kind": "fan"
},
{
"asset_id": "P3",
"kind": "pump"
},
{
"asset_id": "P4",
"kind": "pump"
}
]Follow the operations
1. Asset register
[ { "asset_id": "P1", "site": "north", "kind": "pump" }, { "asset_id": "P2", "site": "north", "kind": "fan" }, { "asset_id": "P3", "site": "south", "kind": "pump" }, { "asset_id": "P4", "site": "south", "kind": "pump" } ]2. Choose table columns
[ { "asset_id": "P1", "kind": "pump" }, { "asset_id": "P2", "kind": "fan" }, { "asset_id": "P3", "kind": "pump" }, { "asset_id": "P4", "kind": "pump" } ]3. Order rows by named fields
[ { "asset_id": "P1", "kind": "pump" }, { "asset_id": "P2", "kind": "fan" }, { "asset_id": "P3", "kind": "pump" }, { "asset_id": "P4", "kind": "pump" } ]4. Result
[ { "asset_id": "P1", "kind": "pump" }, { "asset_id": "P2", "kind": "fan" }, { "asset_id": "P3", "kind": "pump" }, { "asset_id": "P4", "kind": "pump" } ]
The workshop calculates over small record lists to make the relational operations visible. It is not a SQL parser or query planner. The worksheet runs SQLite and is the authority for SQL behaviour, including types and NULL.
1. State the grain in an ordinary sentence
Grain means what one row represents. Here an assets row describes one asset; an inspections row describes one visit. Write that sentence before choosing columns. A minutes field belongs to a visit, because a later visit to the same pump can take a different amount of time.
If a table mixes one row per asset with occasional rows per visit, counting its rows has no consistent meaning. The problem exists even if every cell is neatly formatted.
Ask what event or object caused this row to exist.
2. Give records names that remain useful
A primary key identifies one row and cannot duplicate another key in the same table. We explicitly declare text asset IDs NOT NULL as well, avoiding SQLite’s historical nullable-primary-key exceptions. The inspection key identifies the visit, while its asset_id refers to the asset table.
A foreign key records that relationship. With enforcement enabled, an inspection cannot name a missing asset. It does not prevent several inspections from naming the same asset; that repetition is the intended one-to-many relationship.
A key identifies a record. A descriptive label such as pump describes it.
3. Read a table with SELECT and FROM
SELECT names the fields we want back. FROM names their source table. The database returns a result table; reading it does not change the stored asset register. ORDER BY asset_id asks for a reproducible display order, which we will examine more closely later.
Use single quotes for text values such as north. Column names in these examples are simple identifiers. SQL keywords are shown in capitals for readability, not because they must be capitalised.
Follow the source name to the visible initial table before running the query.
Read the site attached to each asset
The query keeps the asset identity next to its site. No inspection information is needed.
| asset_id | site | kind | active |
|---|---|---|---|
| P1 | north | pump | 1 |
| P2 | north | fan | 1 |
| P3 | south | pump | 0 |
| P4 | south | pump | 1 |
| inspection_id | asset_id | minutes | finding |
|---|---|---|---|
| 101 | P1 | 30 | loose |
| 102 | P1 | NULL | NULL |
| 103 | P2 | 0 | clear |
| 104 | P3 | 20 | clear |
| 105 | P2 | 10 | loose |
| 106 | P1 | 15 | loose |
| reading_id | asset_id | temperature |
|---|---|---|
| 1 | P1 | 70 |
| 2 | P1 | 80 |
| 3 | P2 | NULL |
| 4 | P3 | 65 |
Database setup for this example
CREATE TABLE assets (
asset_id TEXT PRIMARY KEY NOT NULL,
site TEXT NOT NULL,
kind TEXT NOT NULL,
active INTEGER NOT NULL CHECK (active IN (0, 1))
);
CREATE TABLE inspections (
inspection_id INTEGER PRIMARY KEY,
asset_id TEXT NOT NULL REFERENCES assets(asset_id),
minutes INTEGER CHECK (minutes >= 0),
finding TEXT
);
CREATE TABLE readings (
reading_id INTEGER PRIMARY KEY,
asset_id TEXT NOT NULL REFERENCES assets(asset_id),
temperature REAL
);
INSERT INTO assets VALUES
('P1','north','pump',1), ('P2','north','fan',1),
('P3','south','pump',0), ('P4','south','pump',1);
INSERT INTO inspections VALUES
(101,'P1',30,'loose'), (102,'P1',NULL,NULL),
(103,'P2',0,'clear'), (104,'P3',20,'clear'),
(105,'P2',10,'loose'), (106,'P1',15,'loose');
INSERT INTO readings VALUES
(1,'P1',70.0), (2,'P1',80.0), (3,'P2',NULL), (4,'P3',65.0);SELECT asset_id, site
FROM assets
ORDER BY asset_id;| asset_id | site |
|---|---|
| P1 | north |
| P2 | north |
| P3 | south |
| P4 | south |
Each output row still represents one asset.
Check yourself
Questions on Parts 1 to 3
Question 1 of 2
How many assets do those two visits describe?
Use the information below.
assets has P1, kind pump. inspections has visits 101 and 102, both with asset_id P1.4. Choose types that match the stored facts
An identifier such as P1 is text. Inspection minutes are whole numbers. Temperature can have a fractional value. NULL is a marker for a missing value, which we will study separately; it is not a string spelling NULL.
SQLite usually associates a storage class with the value and gives columns an affinity that influences conversion. Other databases commonly enforce declared column types more strictly. SQLite also supports STRICT tables, but our ordinary tables make the distinction worth noticing. Constraints and validation remain necessary when an import can contain unexpected values.
A number stored as text can behave differently in sorting or arithmetic. Inspect actual values rather than inferring their type from how they look.
5. Keep relationships available for the next question
We can answer which kinds of assets exist from the register. To ask how much time each one consumed, we need visits from the inspection log. Keeping those facts separate avoids repeating and potentially disagreeing about the asset’s site on every visit.
Later joins will reconnect the records by their keys. For now, preserve the key when projecting the register. A report containing only pump, fan, pump, pump has lost the information needed to distinguish the three pumps.
The first workshop asks you to retain identity even when descriptions repeat.
Check yourself
Questions on Parts 1 to 5
Question 1 of 2
Where does the new duration belong?
Use the information below.
A new visit to P1 lasted 12 minutes. Existing visit 101 lasted 30 minutes.Try it with Botie
Build with Botie
Write and run SQL