Data Integrity¶
Data integrity is the ability of a database to represent the real world faithfully: to accept only valid data states and only valid transitions between them. Every scientific record encodes rules that hold outside the database โ a mouse has exactly one identifier, a recording belongs to a session that actually happened, a measurement is a number in a known range. Data integrity is the discipline of turning those real-world rules into database constraints that the engine enforces automatically.
The distinguishing property of a constraint is that it cannot be bypassed. Validation written in application code protects only the one program that runs it; a second script or a collaborator's notebook can write straight past it. A constraint declared in the schema is enforced for every client, on every write, with no cooperation required โ the rule lives with the data, not with any one program that touches it.
DataJoint organizes these guarantees into seven types of integrity. The first four are classical relational guarantees; the last three extend them to compositions, concurrency, and computation. Each maps to a specific feature of a table definition.
From real-world rules to database constraints¶
Database design is largely the work of translating a sentence about the world into a set of enforceable constraints. Consider one lab rule:
Every mouse has a unique identifier, and every recording session must reference a real mouse and record when it started.
That single sentence decomposes into four independent constraints, each a different type of integrity:
@schema
class Mouse(dj.Manual):
definition = """
mouse_id : char(6) # unique ear-tag identifier
---
date_of_birth : date
sex : enum('M', 'F', 'U')
"""
@schema
class Session(dj.Manual):
definition = """
-> Mouse # references a real mouse
session_idx : int32
---
session_start : datetime # must be present and a valid timestamp
"""
| Fragment of the rule | Constraint | Integrity type |
|---|---|---|
| "a unique identifier" | primary key on mouse_id |
entity |
| "must reference a real mouse" | foreign key -> Mouse |
referential |
| "record when it started" (a value is required) | session_start is not nullable |
completeness |
| "when it started" is a timestamp | datetime type on session_start |
domain |
No procedural code enforces any of this. The declaration is the enforcement.
The seven types of integrity¶
1. Domain integrity¶
Every value falls within the valid set for its attribute. An attribute's type restricts what it can hold โ a numeric range, a fixed set of categories, a string length, a calendar date. The database rejects anything outside that set at insert time.
DataJoint enforces domain integrity through the attribute type declared for
each column: int32, int64, float32, float64, decimal(p, s),
varchar(N), char(N), date, datetime, uuid, and enum(...) for
closed categorical sets. Choosing sex : enum('M', 'F', 'U') instead of a
free-text field means an invalid category can never be stored, and
decimal(5, 2) fixes both magnitude and precision. The type system is
extensible: custom codecs attach domain rules to structured and object-backed
attributes as well.
2. Completeness¶
Required information is always present. An attribute that must have a value cannot be left empty. This prevents records that are structurally valid but analytically useless โ a session with no start time, a subject with no species.
In DataJoint, every attribute is required by default: unless an attribute is explicitly given a default value (including an explicit nullable default), a value must be supplied on insert. Primary-key attributes are always required. Completeness is thus the normal case, and optionality is the deliberate exception you opt into.
3. Entity integrity¶
Each real-world entity corresponds to exactly one record, and each record to exactly one entity. This bidirectional one-to-one correspondence is what makes a record a trustworthy stand-in for the thing it represents โ no duplicates, no two entities sharing a row.
The primary key โ the attributes declared above the --- separator โ
enforces the database half of this guarantee by rejecting duplicate keys.
DataJoint requires every table to have a primary key and requires key values
to be supplied explicitly (there is no auto-increment), so a client must
commit to which entity it is inserting. The other half โ reliably binding a
physical entity to its identifier โ is a real-world process such as an ear
tag or barcode, which the database mirrors but cannot create. See
Entity Integrity for the full treatment.
4. Referential integrity¶
A reference always points to something that exists. If one entity refers to another, the referenced entity must be present; there are no dangling pointers to records that were never created or have since been removed.
DataJoint declares references with the foreign-key arrow ->, which inherits
the referenced table's primary key into the dependent table and enforces that
the referenced row exists before a dependent row can be inserted. Removal is
symmetric: deleting an entity cascades to everything that depends on it,
so the database never enters a state with orphaned references. This is a
row-level guarantee: objects held in integrated object storage are not removed
synchronously with their rows (hash-addressed storage is deduplicated, and
immediate deletion is unsafe under concurrency) โ they are reclaimed
asynchronously by garbage collection. See
Referential Integrity.
5. Compositional integrity¶
A multi-part entity is stored whole or not at all. Some entities are inherently composite: a segmentation and all of its detected cells, an import and every frame it produced. Storing the container without its parts โ or some parts without others โ would leave a partial, misleading entity.
DataJoint expresses this with the master-part relationship. The master
represents the composite entity; its part tables hold the components produced
with it. When a master and its parts are populated together by make()
(Computed and Imported tables), their insertion is one transaction, so a partial
composite is never committed; for Manual master-part sets this atomicity is
maintained by convention (a single transaction). Deletion cascades from master
to parts in either case. See
Master-Part.
6. Consistency¶
Concurrent operations never leave the database in an invalid intermediate state. With many users and long-running computations writing at once, a multi-step operation must either complete entirely or leave no trace, and in-progress work must not be visible to others as if it were final.
DataJoint relies on the backend's transactions and ACID guarantees to enforce this. A group of writes is committed atomically or rolled back as a unit, and isolation keeps a partially applied change from being seen by a concurrent reader. Both supported backends โ MySQL and PostgreSQL โ provide these guarantees. See Transactions.
7. Workflow integrity¶
Entities come into being in a valid order. This extends referential integrity along the axis of time. Referential integrity says a recording must reference a real session; workflow integrity adds that the session must exist before the recording, and that a computed result cannot exist before the inputs it was derived from.
The same foreign-key graph that enforces referential integrity is a
directed acyclic graph (DAG) that prescribes execution order. Because it
is acyclic, there is always a valid sequence in which entities can be
created. populate() walks this graph, computing each entity only after its
upstream dependencies exist, and cascade deletes remove downstream results
when their inputs become invalid. The dependency graph and the pipeline are
the same object, which is why results stay traceable to their declared inputs
and remain reproducible. See
The Relational Workflow Model.
Why declared constraints matter¶
The seven types share one property that application-level checks cannot match: they are enforced by the database for every client, always.
- Unbypassable โ no program can write invalid data, however it connects.
- Automatic โ the engine checks every write; no developer has to remember to call a validator.
- Concurrent-safe โ the guarantees hold under simultaneous access by many users and processes.
- Self-describing โ the schema states the rules explicitly, so the constraints double as documentation that both people and agents can read.
A schema that declares its integrity rules is an executable specification of what valid data looks like: invalid states are refused at the point of entry rather than discovered later in analysis.
See also¶
- Entity Integrity โ the one-to-one correspondence and the primary key
- Referential Integrity โ foreign keys and cascading deletes
- Master-Part โ compositional integrity through transactional grouping
- Normalization โ organizing attributes so each describes one entity
- Transactions โ atomicity and consistency under concurrent access
- The Relational Workflow Model โ the frame in which these guarantees fit together