What Should Botie Label Each Reading?
Express a row-level decision with CASE while keeping unknown measurements visible and using fallbacks deliberately.
13 sections · 4 questions · 1 coding challenge
The arrow keys move between sections too.
A Query Result Can Include a Decision
BOTIE
CASE does not update the sensor record. It adds a computed label to the result, which means the policy can be inspected and changed without overwriting the observed temperature.
Sources for this history
The Reading Needs a Label, Not Another Spreadsheet Formula
The maintenance dashboard shows temperature readings. The team wants inspect at or above 75, monitor below that point, and unknown when no value was recorded. Botie should apply the same policy wherever the report is read.
SQL can derive that label beside each reading. We will write the conditions in an order that preserves the missing case, then check the exact threshold rather than relying on a few obvious examples.
These maintenance records are invented. The workshop exposes record operations; the SQL worksheet runs actual SQLite queries.
Step 1 of 1
Check the boundary and the missing case
Policy: unknown for NULL, inspect at 75 or more, otherwise monitor.
| Temperature | First applicable condition | Label |
|---|---|---|
| NULL | Value is missing | unknown |
| 74 | Below the threshold | monitor |
| 75 | At or above the threshold | inspect |
NULL
- First applicable condition
- Value is missing
- Label
- unknown
74
- First applicable condition
- Below the threshold
- Label
- monitor
75
- First applicable condition
- At or above the threshold
- Label
- inspect
Follow the same records into the query
The derived column keeps the reading grain and states how missing measurements are handled.
Keep the measurement beside the derived label
Use a threshold-label component, then choose the reading identity and result label.
BOTIE
Reading labels
[
{
"reading_id": 1,
"action": "monitor"
},
{
"reading_id": 2,
"action": "inspect"
},
{
"reading_id": 3,
"action": "unknown"
},
{
"reading_id": 4,
"action": "monitor"
}
]Follow the operations
1. Temperature readings
[ { "reading_id": 1, "asset_id": "P1", "temperature": 70 }, { "reading_id": 2, "asset_id": "P1", "temperature": 80 }, { "reading_id": 3, "asset_id": "P2", "temperature": null }, { "reading_id": 4, "asset_id": "P3", "temperature": 65 } ]2. Set a constant
75
3. Label a measured threshold
[ { "reading_id": 1, "asset_id": "P1", "temperature": 70, "action": "monitor" }, { "reading_id": 2, "asset_id": "P1", "temperature": 80, "action": "inspect" }, { "reading_id": 3, "asset_id": "P2", "temperature": null, "action": "unknown" }, { "reading_id": 4, "asset_id": "P3", "temperature": 65, "action": "monitor" } ]4. Choose table columns
[ { "reading_id": 1, "action": "monitor" }, { "reading_id": 2, "action": "inspect" }, { "reading_id": 3, "action": "unknown" }, { "reading_id": 4, "action": "monitor" } ]5. Order rows by named fields
[ { "reading_id": 1, "action": "monitor" }, { "reading_id": 2, "action": "inspect" }, { "reading_id": 3, "action": "unknown" }, { "reading_id": 4, "action": "monitor" } ]6. Result
[ { "reading_id": 1, "action": "monitor" }, { "reading_id": 2, "action": "inspect" }, { "reading_id": 3, "action": "unknown" }, { "reading_id": 4, "action": "monitor" } ]
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. Write the policy before its syntax
A label must mean something specific. Our threshold is an invented teaching rule, not a diagnosis of equipment health. The known temperature and the missing marker lead to different branches.
Keep the temperature in the source table. A label is a derived interpretation that may change when the maintenance policy changes. Storing only the label would discard the measurement needed to recompute it.
For a model pipeline, the policy version may matter as much as the label itself.
2. Take the first condition that is true
A searched CASE expression checks WHEN conditions and returns the value after the first matching THEN. ELSE supplies the fallback when no condition is true. Without ELSE, that fallback is NULL.
Order overlapping conditions from the intended specific case to the broader case. If we later added an emergency threshold above the inspection threshold, putting inspect first could consume readings that should have reached the emergency branch.
CASE is a decision sequence. The order of overlapping rules is part of its meaning.
3. Keep the unknown branch explicit
A missing temperature does not satisfy temperature >= 75. If ELSE says monitor, an unrecorded reading could therefore appear to be safe to monitor. Put temperature IS NULL in its own branch.
The alias gives the resulting label a useful column name. Each input reading still produces one result row, so this operation preserves the reading grain.
A sensible default for known values can be a misleading default for absent evidence.
Make a display fallback without changing the finding
COALESCE returns the first non-null argument. It gives the display text for an absent finding while leaving the stored field untouched.
| 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 inspection_id, COALESCE(finding, 'awaiting entry') AS finding_label
FROM inspections WHERE inspection_id <= 103 ORDER BY inspection_id;| inspection_id | finding_label |
|---|---|
| 101 | loose |
| 102 | awaiting entry |
| 103 | clear |
The fallback describes the missing entry; it does not invent an inspection result.
Check yourself
Questions on Parts 1 to 3
Question 1 of 2
Which label applies?
Use the information below.
CASE returns unknown for NULL, inspect for temperature >= 75, otherwise monitor. Temperature is 75.4. Use compatible result meanings across branches
CASE returns a single expression value. Keep its branches consistent with the meaning and type the next component expects. A column mixing a number of minutes with a text error message makes later analysis awkward and may behave differently across SQL engines.
Prefer a measured-value column and a separate status column when both are needed. COALESCE is useful for a deliberate fallback, while CASE is useful when the decision depends on a condition beyond simple missingness.
A status such as unknown should not be substituted into a numeric measurement column.
5. Test equality at the threshold
A reading of 75 must receive inspect under our at-or-above rule. A strict greater-than condition would mislabel exactly that reading while passing tests with 70 and 80.
Add a changed case at the boundary and another missing value. Those examples distinguish the actual rule from an implementation that merely agrees with the initial handful of rows.
Boundary cases reveal decisions that ordinary examples may leave ambiguous.
6. Keep labels separate from actions
The SQL result is a report. Producing the word inspect does not itself schedule a technician or authorise work on a customer’s equipment. An application acting on the label needs its own permission, review and execution rules.
For analysis, also check when the reading became available. A label derived from future observations cannot be used as a feature for an earlier prediction without leakage.
A computed label is one input to a decision process, with a definition the next stage must understand.
Check yourself
Questions on Parts 1 to 6
Question 1 of 2
What changes?
Use the information below.
A stored finding is NULL. COALESCE(finding, awaiting entry) is selected.Try it with Botie
Build with Botie
Write and run SQL