Data Quality Dimensions

"The data is bad" is not actionable. Data quality dimensions break the vague complaint into specific, measurable properties, each with its own checks and owners. This page defines the six dimensions used in most frameworks, shows how to turn each one into a metric, where to run the checks in a pipeline, and how to avoid the common trap of hundreds of alerts that nobody reads.


1. What the Dimensions Are

Six Core Dimensions of Data Quality Accuracy matches the real world Completeness nothing missing Consistency agrees across systems Timeliness fresh enough to use Validity fits format and rules Uniqueness no duplicates Accuracy is the goal; the other five are cheaper, measurable proxies that catch most failures.

Different frameworks (DAMA DMBOK, ISO 8000, ISO/IEC 25012) list between six and more than a dozen dimensions, but six appear almost everywhere. Accuracy: values correctly describe the real-world entity. Completeness: required records and fields are present. Consistency: the same fact has the same value across tables and systems. Timeliness: data is available and current when needed. Validity: values conform to types, formats, ranges and business rules. Uniqueness: each real-world entity is recorded once.

Quality is always relative to a use. A table that is fresh enough for a monthly report may be far too stale for fraud detection. That is why thresholds belong to the consumer's needs, ideally written into a data contract.

2. Why It Matters

Bad data costs twice: once when a wrong decision or model is made, and again when engineers spend days tracing the cause. Naming the dimension speeds up both prevention and diagnosis. "Completeness of email dropped from 99.8% to 71% after Tuesday's deploy" points straight to a fix; "the marketing numbers look off" does not. Dimensions also make quality reportable, so data owners can set targets and track them like any other service level.

3. Each Dimension in One Table

One Bad Table, Six Kinds of Problem order_id email country amount updated 1001 ana@x.com US 19.99 today 1002 bob(at)x US 250000.00 today 1002 bob(at)x USA 25.00 today 1004 (null) DE 12.50 9 days ago validity: bad email accuracy: 250000 likely typo uniqueness: 1002 twice consistency: US vs USA completeness: null email timeliness: stale row

A single small table can fail every dimension. The email bob(at)x is invalid. An amount of 250,000 on a shop selling 20-dollar items is probably inaccurate, a typo for 25.00, but no rule can prove it; accuracy usually needs a trusted reference or a human. Order 1002 appears twice, breaking uniqueness. US and USA encode the same country differently, a consistency problem that will split group-by results. The null email is a completeness gap, and the row last updated nine days ago fails a one-day timeliness requirement.

4. How to Measure Each Dimension

DimensionTypical metricExample check
Accuracy% matching a trusted source; outlier rateCompare sample to system of record; flag values beyond robust bounds
Completeness% non-null; row count vs expectedemail IS NOT NULL ≥ 99%; today's rows within 20% of 7-day average
Consistency% agreeing across systems or with reference dataOrder totals equal sum of line items; country in ISO list
TimelinessAge of newest record; delivery delaymax(updated_at) within 1 hour
Validity% passing format, type, range, enum rulesEmail regex, amount ≥ 0, status in allowed set
UniquenessDuplicate rate on business keyCOUNT(DISTINCT id) = COUNT(*)

Express each metric as a ratio between 0 and 1 where possible, so they can be trended and compared. Then set a threshold per dataset based on what consumers need, not on what the data currently achieves.

5. Where Checks Run

Where Checks Run in a Pipeline Sources Ingest checks schema, nulls, volume Transform checks keys, joins, rules Publish gate freshness, metrics On failure: quarantine, alert owner, block publish

Run cheap structural checks (schema, nulls, row counts) at ingestion, so bad loads are caught before they spread. Run relational and business-rule checks (referential integrity, uniqueness after joins, totals that must reconcile) after transformation. Run freshness and key business metrics at the publish gate, just before consumers see the data. Failures should quarantine rows or block publishing, and alert the team that owns the source; lineage tells you which downstream assets to mark as affected.

6. Worked Example: Checks in pandas and SQL

import pandas as pd

df = pd.DataFrame({
    "order_id": [1001, 1002, 1002, 1004],
    "email":    ["ana@x.com", "bob(at)x", "bob(at)x", None],
    "country":  ["US", "US", "USA", "DE"],
    "amount":   [19.99, 250000.00, 25.00, 12.50],
    "updated":  pd.to_datetime(["2026-10-01", "2026-10-01", "2026-10-01", "2026-09-22"]),
})
now = pd.Timestamp("2026-10-01 12:00")
ref_countries = {"US", "DE", "GB", "FR"}          # ISO-3166 alpha-2 subset

checks = {
    "completeness: email not null":  df["email"].notna().mean(),
    "validity: email format":        df["email"].dropna().str.match(r"^[^@\s]+@[^@\s]+\.\w+$").mean(),
    "uniqueness: order_id":          1 - df["order_id"].duplicated().mean(),
    "consistency: country in ref":   df["country"].isin(ref_countries).mean(),
    "timeliness: updated < 24h":     (now - df["updated"] < pd.Timedelta("24h")).mean(),
    # accuracy proxy: flag outliers vs a robust range (median +/- 10 * MAD)
    "accuracy proxy: amount plausible": (
        (df["amount"] - df["amount"].median()).abs()
        <= 10 * (df["amount"] - df["amount"].median()).abs().median()).mean(),
}
THRESHOLD = 0.99
for name, score in checks.items():
    status = "PASS" if score >= THRESHOLD else "FAIL"
    print(f"{status}  {score:6.1%}  {name}")

Each check returns a score between 0 and 1 and is compared with a threshold. On the sample data, every dimension fails, matching the annotated figure above. The accuracy check is a proxy: it flags implausible amounts using a robust spread (median absolute deviation), but only a reference source can confirm a value is truly wrong. The same metrics expressed in SQL:

-- The same checks as SQL, runnable in most warehouses (one row per metric)
SELECT 'completeness_email' AS metric, AVG(CASE WHEN email IS NOT NULL THEN 1.0 ELSE 0 END) AS score FROM orders
UNION ALL
SELECT 'uniqueness_order_id', COUNT(DISTINCT order_id) * 1.0 / COUNT(*) FROM orders
UNION ALL
SELECT 'consistency_country', AVG(CASE WHEN country IN (SELECT code FROM ref_country) THEN 1.0 ELSE 0 END) FROM orders
UNION ALL
SELECT 'timeliness_hours_since_newest',
       EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - MAX(updated))) / 3600 FROM orders;

Tools such as Great Expectations, Soda, dbt tests, Deequ (for Spark) and Databricks expectations package these checks with scheduling, history and alerting. The concepts are the same.

7. Rules vs Anomaly Detection

Rule-based checks encode what you know: an amount is never negative, an ID is unique. They are precise and explainable but only catch failures you anticipated. Anomaly-based monitoring learns the normal range of metrics such as row count, null rate, or the distribution of a column, and alerts on deviations. It catches surprises, like a silent 40% drop in volume after an upstream change, but produces more false alarms and needs history to learn from. Most mature teams combine them: explicit rules on critical fields, plus automated volume, freshness and distribution monitors on every important table.

8. From Checks to Scorecards and SLOs

Individual checks answer "is this batch OK?". Owners and consumers also need a longer view. Store every check result with a timestamp, dataset, dimension and score, and you can build a scorecard per dataset showing each dimension over time. That history turns quality into service levels: for example, "orders is complete to 99.9% and fresh within one hour on 99% of days this quarter". Consumers can then decide whether a dataset is fit for their purpose before they build on it, and owners can see whether quality is improving or slowly degrading.

Weight the scorecard by impact rather than averaging everything. A 2% null rate on an optional marketing field matters far less than a 0.1% duplicate rate on payment IDs. Many teams publish a simple status per dataset in the catalog (healthy, degraded, failing), derived from the critical checks only, which is easier to act on than a single blended score.

9. Pitfalls

11. Further Reading