In short
A rule set gathers checks to run against a database. Created from a schema, it starts with hundreds of checks drawn from that schema’s structure and mapping — ordinary checks, which you edit or delete like your own. A scan runs them all and produces a score; when a check fails, Investigate shows the rows at fault.
What it is for
A warehouse degrades without warning. A feed changes, a column empties, an identifier stops matching. Nothing crashes: the dashboards keep showing numbers, only wrong ones.
A rule set turns that watch into something you rerun. Rather than discovering the problem in an analysis six months later, you catch it by running a scan again.
This is a workspace feature
Rule sets live in the warehouse, not in a project. A database’s quality matters to everyone who uses it: checking it once per database makes more sense than re-checking it in every project.
Creating a rule set from a schema
New rule set opens a form: a Name, an Identifier, the Database to check and the Schema to start from. Picking the database proposes its schema automatically; the form then tells you how many checks will be added, and where they come from.
These checks are written once, at creation. From then on they are checks like any other: you rename them, edit their SQL, disable or delete them. Choosing None (empty rule set) starts with no checks.
From the DDL — structure, on the raw tables
The DDL is the description of the schema’s tables (columns, constraints). From it Linkr checks, for each table: do the columns exist, is every NOT NULL column actually filled, is the primary key unique, does every foreign key point to a row that exists.
From the mapping — what the data means
The schema’s mapping says which table holds patients, stays, events. From it Linkr checks: are the essential columns filled, are there stays or events with no patient, an end before the start, an event before birth or more than 60 days after death, an age outside 0–130, unmapped codes, values with no unit, negative doses.
On OMOP CDM 5.4, that comes to about 470 checks. A mapping check that a DDL constraint already covers is not created twice.
Why check a NOT NULL the DDL already declares?
Because declaring it guarantees nothing. A database made of Parquet or CSV files enforces no constraint, and OMOP loads often skip the constraint scripts to go faster. The check costs little; an empty mandatory column skews every analysis that relies on it.
Mapping checks follow each database
They are written against Linkr’s standard relations (linkr_patient, linkr_visit, linkr_event_…), not against the source tables. The same rule set therefore applies to two databases sharing a schema, even if one of them adapted its mapping.
Conformance, completeness, plausibility
Every check has a category and, for two of them, a subcategory. They follow the framework of Kahn et al. (2016), the one OHDSI’s Data Quality Dashboard uses — the shared vocabulary of the OMOP ecosystem.
| Category | The question | Subcategories |
|---|---|---|
| Conformance | Does the data follow the expected structure and formats? | Value, Relational, Computational |
| Completeness | Is the data there where it is expected? | — |
| Plausibility | Are the values believable? | Uniqueness, Atemporal, Temporal |
The information icon next to Category, in the editor, recalls these definitions. For what data quality means in healthcare, see the article Data quality.
Organising checks
In the Checks tab, the list is arranged in groups. Generated checks land in their table’s group; the ones you write, in Other checks. A dot gives each check’s category — blue for conformance, teal for completeness, fuchsia for plausibility — and a group header counts its enabled checks out of the total.
Failed
12 rows break the rule out of 48,210 (0.02 %)
Threshold 0 %: no violation allowed
- Rename or Delete group — right-click the group header. Deleting asks for confirmation: delete its checks with it, or Keep the checks, which move to “Other checks”.
- New group — in the list’s + menu.
- Move to… — right-click a check, then pick the destination group, “Other checks” or a new group to name. With Select multiple, you move, enable, disable or delete several checks at once.
- Filter and sort — the filter button narrows the list by Origin (from the DDL, from the mapping, or manual) and by Category, and sorts the groups: List order, A → Z or Z → A.
A disabled check stays in the list, struck through, but is left out of the scan and the score.
An empty group is kept
A group created with New group stays in the list, even empty, until a check is moved into it: it shows “Empty — right-click a check › Move to…”. It is saved with the rule set and travels with it.
Adding checks
The list’s + menu offers three entries:
- Manual check — an empty check, with an example query to adapt.
- From the DDL… and From the mapping… — the schema’s checks that this rule set does not hold yet, grouped by table, to tick. This is how you get back a check deleted by mistake, or bring a rule set up to date after the schema changed.
Writing a check
A check is a SQL query under a single constraint: return one row with two columns named violated_rows (the rows breaking the rule) and total_rows (the rows the rule applies to).
SELECT
COUNT(*) FILTER (WHERE end_datetime < start_datetime)::BIGINT AS violated_rows,
COUNT(*)::BIGINT AS total_rows
FROM linkr_visit
Linkr does not read your SQL: it reads those two numbers, turns them into a percentage, and compares it with the threshold. All the business logic is yours.
The editor — on the right in the figure above — has two tabs. Count holds that query. Failing rows, optional, holds the one listing the rows the count treats as violations — the one Investigate opens. Generated checks have both. An orange dot marks an unsaved change, and Save or Cancel apply to both queries at once.
Four settings come with the queries:
- Category and Subcategory — used for grouping and charts.
- Severity — error, warning or notice.
- Threshold (%) — the percentage of failing rows that is tolerated, between 0 and 100.
0means zero tolerance. An empty value, or one outside that range, is flagged beside the field and not saved.
The Test button, or Ctrl/⌘ + Enter in the Count tab, runs the check without launching a full scan and explains the result: how many rows break the rule, out of how many, and the threshold applied. The same shortcut in the Failing rows tab runs Investigate. With no database picked, a red message says so beside the database selector.
Investigating a failure
A failing check says how many rows are wrong, not which ones. Investigate — in the Checks tab toolbar, or in a result’s detail panel — opens the Investigation tab and runs the failing-rows query there: you see the offending records straight away. Without that query, the count query is opened instead.
The Investigation tab is a SQL console on the rule set’s database: you can rewrite the query, run it again with Run, Ctrl/⌘ + Enter (selection or current line) or Ctrl/⌘ + Shift + Enter (the whole editor). Every run shows the number of rows, the duration, and the date and time it ran.
Running a scan and reading the result
Run Scan, in the Results tab, runs every enabled check, one after the other. Above the table sit two charts — Checks by category and Failure rate by category — which the Charts button hides or shows.
| Status | Check | Category | Subcategory | Table | % Violated | Severity |
|---|---|---|---|---|---|---|
| linkr_event_measurement: values with a unit | Plausibility | Atemporal | linkr_event_measurement | 7.4% | ||
| linkr_visit: end after start | Plausibility | Temporal | linkr_visit | 0.0% | ||
| note: columns | Conformance | Value | note | 0.0% | ||
| gender_concept_id not null | Completeness | — | person | 0.0% | ||
| person: unique key | Plausibility | Uniqueness | person | 0.0% | ||
| visit_occurrence_id → visit_occurrence | Conformance | Relational | measurement | 0.0% | ||
| linkr_event_drug: no negative dose | Plausibility | Atemporal | linkr_event_drug | — |
linkr_event_measurement: values with a unit
Numeric values of linkr_event_measurement come with a unit
For each check, the table gives: status (Pass, Fail, Error or N/A), name, category, subcategory, table, percentage failing and severity; hovering an icon gives its label. Clicking a row opens the detail panel: the violation bar against the threshold, the counters, the execution time, SQL Query — which shows both queries — and Investigate.
The score is the proportion of checks that passed among those that apply. Checks with nothing to test — zero rows concerned, N/A — are excluded. Its badge is green from 95 %, amber from 80 %, red below.
A check in error lowers the score
A check whose SQL fails — a missing table, a renamed column, a typo — counts in the total but never among the passes. The score therefore drops as if the data were at fault. When a score falls for no visible reason, look first for rows with status Error: the detail panel gives the message.
Every scan is recorded in the Run History, which you can browse, replay on screen, and clear. A single Test is not: only full scans are kept.
What to know before launching
- A scan can be long. Checks run in sequence, and a rule set created from OMOP holds several hundred. Disable the ones that do not concern you rather than letting them slow every scan.
- A database is essential. It is optional when creating the rule set, but without one there is nothing to run.
- Where the queries run. In browser mode, in the browser. In server mode, on the server, against the database: the browser only sequences the checks and never loads the raw data. See Deployment modes.
- The history keeps the full report of every scan. With frequent scans it grows: that is what the Clear button is for.
Sharing a rule set
A rule set is an entity like any other: it exports, is versioned in a git repository, and is published to the catalog. Its export holds all its checks — generated and manual, with both queries, their group and whether they are enabled — and a pointer to the schema it came from. Never the results, nor the data.
On import, the checked database is found again by its identity, if it is installed. This is what lets a proven set of OMOP quality checks travel between institutions and apply as-is to another database carrying the same schema. See Entities and sharing.
Going further
- Schemas — the DDL and the mapping the checks are drawn from.
- Databases — attaching a schema to a database, and its SQL console.
- Data quality — why raw data is not ready, and what it takes.
- ETL pipelines — which have their own source/target comparison.