Less Than Field Check Examples
Three real-world scenarios that show how the Less Than Field check is typically used in production: ordering a contract term, capping a used amount against a limit with a filter, and comparing two timestamps with a duration tolerance. The first two run at 100% coverage and report Record Anomalies; the third lowers coverage and reports a Shape Anomaly instead.
The situation: A contract cannot end before it starts. Rows where start_date is later than end_date come from a migration that swapped the two columns. A single-day contract is valid, so equal dates must pass and Inclusive is on.
Check configuration
| Field | Value |
|---|---|
| Rule | Less Than Field |
| Field | start_date |
| Filter | (none) |
| Custom Anomaly Description | Off |
| Field to compare | end_date |
| Inclusive | On |
| Comparators | (none) |
| Coverage | 100% |
| Owner | (check creator) |
| Anomaly Assignee | (Contracts Data team) |
| Description | Start date must be earlier than the end date. |
| Tags | contracts, integrity |
| Additional Metadata | jira: DATA-8901 |
| Status | Active |
Payload
{
"description": "Start date must be earlier than the end date.",
"rule": "lessThanField",
"fields": ["start_date"],
"container_id": 145,
"coverage": 1,
"filter": null,
"properties": {"field_name": "end_date", "inclusive": true},
"tags": ["contracts", "integrity"],
"additional_metadata": {"jira": "DATA-8901"},
"anomaly_message_field": null,
"template_id": null,
"status": "Active",
"owner_id": 7,
"default_anomaly_assignee_id": 12
}
Sample Data
| contract_id | start_date | end_date |
|---|---|---|
| C-001 | 2026-01-01 | 2026-12-31 |
| C-002 | 2026-08-10 | 2026-03-01 |
| C-003 | 2026-04-15 | 2026-04-15 |
| C-004 | 2026-06-01 | (null) |
What gets flagged
Contract C-002 starts after it ends, the signature of swapped columns. C-003 is a single-day contract and passes, because Inclusive is on. C-004 has no end date yet, so there is nothing to compare and the row passes. Coverage is 100%, so the failing row is reported as a Record Anomaly.
Record Anomaly
The field 'start_date' has value 2026-08-10, which is not less than the value of 'end_date'
Flowchart
graph TD
A["No filter, evaluate all rows"] --> B["Read start_date and end_date"]
B --> C{"Is either value NULL?"}
C -->|Yes| D["Row passes"]
C -->|No| E{"Is start_date <= end_date?"}
E -->|Yes| D
E -->|No| G["Flag row.<br/>Record Anomaly per failing row."]
Equivalent SQL
The situation: Metered accounts must not consume more than their provisioned quota. Unmetered accounts have no quota and are excluded with a filter. Consuming exactly the quota is allowed, so Inclusive is on.
Check configuration
| Field | Value |
|---|---|
| Rule | Less Than Field |
| Field | used_units |
| Filter | metered = true |
| Custom Anomaly Description | Off |
| Field to compare | quota_units |
| Inclusive | On |
| Comparators | (none) |
| Coverage | 100% |
| Owner | (check creator) |
| Anomaly Assignee | (Billing Operations) |
| Description | Metered usage must stay within the provisioned quota. |
| Tags | billing, integrity |
| Additional Metadata | jira: DATA-8944 |
| Status | Active |
Payload
{
"description": "Metered usage must stay within the provisioned quota.",
"rule": "lessThanField",
"fields": ["used_units"],
"container_id": 512,
"coverage": 1,
"filter": "metered = true",
"properties": {"field_name": "quota_units", "inclusive": true},
"tags": ["billing", "integrity"],
"additional_metadata": {"jira": "DATA-8944"},
"anomaly_message_field": null,
"template_id": null,
"status": "Active",
"owner_id": 7,
"default_anomaly_assignee_id": 29
}
Sample Data (filtered to metered = true)
| account_id | metered | quota_units | used_units |
|---|---|---|---|
| A-01 | true | 1000 | 640 |
| A-02 | true | 500 | 870 |
| A-03 | true | 250 | 250 |
Why the filter matters
The filter runs before the comparison, so unmetered accounts, which have no quota to respect, are never evaluated.
What gets flagged
A-02 consumed well beyond its quota, which the metering job should have blocked. A-03 used exactly its quota and passes, because Inclusive is on. Coverage is 100%, so the failure is reported as a Record Anomaly, and the message ends with the filter that scoped the evaluation.
Record Anomaly
The field 'used_units' has value 870, which is not less than the value of 'quota_units' [filter: metered = true]
Flowchart
graph TD
A["Apply filter: metered = true"] --> B["Read used_units and quota_units"]
B --> C{"Is either value NULL?"}
C -->|Yes| D["Row passes"]
C -->|No| E{"Is used_units <= quota_units?"}
E -->|Yes| D
E -->|No| F["Flag row.<br/>Anomaly message ends with<br/>[filter: metered = true]"]
Equivalent SQL
The situation: An event must be received no later than it was created, allowing for the clock skew between the producer and the collector. A duration tolerance of 5 seconds absorbs that skew. A collector fix is rolling out, so the check tolerates up to 0.5% failures.
Check configuration
| Field | Value |
|---|---|
| Rule | Less Than Field |
| Field | created_at |
| Filter | (none) |
| Custom Anomaly Description | Off |
| Field to compare | received_at |
| Inclusive | On |
| Comparators | Datetime: 5 seconds |
| Coverage | 99.5% |
| Owner | (check creator) |
| Anomaly Assignee | (Event Platform) |
| Description | Events must be created no later than they are received, allowing for clock skew. |
| Tags | events, integrity |
| Additional Metadata | jira: DATA-8988 |
| Status | Active |
Payload
{
"description": "Events must be created no later than they are received, allowing for clock skew.",
"rule": "lessThanField",
"fields": ["created_at"],
"container_id": 733,
"coverage": 0.995,
"filter": null,
"properties": {"field_name": "received_at", "inclusive": true, "duration_comparator": {"millis": 5000}},
"tags": ["events", "integrity"],
"additional_metadata": {"jira": "DATA-8988"},
"anomaly_message_field": null,
"template_id": null,
"status": "Active",
"owner_id": 7,
"default_anomaly_assignee_id": 47
}
Sample Data
| event_id | created_at | received_at |
|---|---|---|
| E-01 | 2026-07-01 10:00:00 | 2026-07-01 10:00:02 |
| E-02 | 2026-07-01 10:00:04 | 2026-07-01 10:00:01 |
| E-03 | 2026-07-01 11:30:00 | 2026-07-01 10:05:00 |
| E-04 | 2026-07-02 09:00:00 | 2026-07-01 10:06:00 |
What gets flagged
E-02 was created three seconds after it was received, which falls inside the 5-second skew tolerance and passes. E-03 and E-04 are ahead by more than an hour and a day, far beyond any clock skew, and fail. Only 50% of the rows pass, which is below the 99.5% coverage threshold, so the check reports a single Shape Anomaly for the dataset with a sample of the offending rows. Coverage below 100% does not produce Record Anomalies.
Shape Anomaly
For the field 'created_at', 50.000% of 4 records (2) are not less than 'received_at'
Flowchart
graph TD
A["No filter, evaluate all rows"] --> B["Read created_at and received_at"]
B --> C{"Are both values NULL?"}
C -->|Yes| D["Row passes"]
C -->|No| H{"Is exactly one value NULL?"}
H -->|Yes| G
H -->|No| E{"Is created_at, within the<br/>5-second tolerance, <= received_at?"}
E -->|Yes| D
E -->|No| G["Flag row.<br/>Passing rate falls below the 99.5%<br/>coverage, so one Shape Anomaly is reported."]
Equivalent SQL
-- Rows the check would flag, allowing 5 seconds of skew.
-- With a comparator configured, a row where exactly one side is NULL also fails.
SELECT e.*
FROM events e
WHERE (e.created_at IS NULL) <> (e.received_at IS NULL)
OR (e.created_at IS NOT NULL
AND e.received_at IS NOT NULL
AND e.created_at - INTERVAL 5 SECONDS > e.received_at);
See Also
-
How It Works
The complete reference: definition, field scope, the compared field, comparators, NULL handling, filter behavior, and coverage.
-
Anomaly Reporting
The anomaly messages the check produces, what the numbers mean, Source Records highlighting, and Custom Anomaly Description.
-
Best Practices
Guidelines for choosing the compared field, using tolerance, and keeping the signal clean.
-
Permissions
The team permission each action needs: view, create, edit, archive, restore, and delete.