Data Quality Rules in T-SQL

A load can finish on time and still publish the wrong data. Data quality rules make the definition of acceptable rows visible and executable in T-SQL.

A metal sieve over a mixing bowl, flour passing through while a few lumps and a small pebble stay in the mesh.

Write Data Quality Rules as Questions

Start with data quality rules the business can explain. Are required keys present? Are dates valid? Does each order point to a known customer? Are amounts within an agreed range? Each rule should have a name, severity, owner, and query that identifies violating rows.

I avoid a single Boolean called IsValid when a row can break several rules. Save the rule name and source row identifier for every violation. That gives the source owner a concrete correction list. It also lets the load distinguish a warning from a condition that blocks publication.

Ask the reader of a report what would make its numbers untrustworthy. That answer is usually more useful than a generic catalog of checks. A quality rule exists to protect a decision, not to decorate a pipeline dashboard.

Check Keys and Required Values First

Missing business keys make deduplication and replay unsafe. Find null or blank keys before applying target changes. Check uniqueness at the intended grain, not at the stage row identity. A unique identity value can coexist with duplicate customer IDs.

Use constraints in the final table as the last defense. Stage queries provide better diagnostics because they can list every offending row. A constraint error alone tells you where the transaction stopped, not the full correction set.

I count violations and inspect examples before a new rule becomes a hard gate. The count is measured by the query, not invented in the blog post. The result tells you whether the source has a current problem or whether the rule itself needs a clearer definition.

SELECT SourceRowId, CustomerCode
FROM dbo.StageCustomer
WHERE NULLIF(TRIM(CustomerCode), N'') IS NULL;
SELECT CustomerCode, COUNT(*) AS duplicate_count
FROM dbo.StageCustomer
GROUP BY CustomerCode
HAVING COUNT(*) > 1;

Validate Types Before Conversion

TRY_CONVERT is useful when a source sends dates and numbers as text. A failed conversion returns NULL instead of aborting the whole query. But a source NULL and an invalid value also need different handling. Check the original text before classifying the result.

Do not depend on ambiguous date formats. Ask for ISO style input, or use a documented conversion style for the source format. A date like 03/04/2025 means different things to different systems. SQL Server should not have to guess which calendar the feed intended.

Keep rejected source values intact. Trimming and parsing into clean columns is fine, but do not overwrite the original text before review. I want the operator to see the exact value that arrived. It makes the conversation with the source owner much shorter.

SELECT SourceRowId, RawOrderDate
FROM dbo.StageOrder
WHERE NULLIF(TRIM(RawOrderDate), N'') IS NOT NULL
  AND TRY_CONVERT(date, RawOrderDate, 23) IS NULL;
From staged rows to a publish decision: a diagram about the data quality rules

Store Data Quality Rules Carefully

A rule table can hold RuleId, name, severity, active status, owner, and a description. It should not automatically execute arbitrary SQL text supplied through an editable field. That design turns a data quality catalog into an unrestricted code runner. Keep executable checks in reviewed procedures or views and link them to rule metadata.

Version the definition when a rule changes. A load from last month should remain explainable under the rule version that ran then. Record RuleId and version with each violation. If thresholds are configurable, store the value and effective dates so the result can be reproduced.

A rule called BadData is not a rule. A name such as MissingCustomerCode or InvalidOrderDate tells the next person what to fix. Plain names are part of operational quality. They keep alerts useful when somebody is reading them before coffee.

Keep a Reject Table With Evidence

The reject table should retain RunId, source row ID, rule ID, original value or raw payload reference, detected time, and disposition. A row can violate more than one rule. Store one violation per row and rule if you need complete diagnosis, then decide whether the row itself can be corrected and replayed.

Protect sensitive values in the reject table. Retain only what support needs, and apply the same access and retention controls as the source data. A reject table is still a data store. It can become a second ungoverned copy if nobody owns it.

I keep accepted and rejected counts separate. Their sum should reconcile to the staged count after accounting for intentional filters. If it does not, investigate the missing category. A quality process that loses rows between trays has become a quality problem itself.

Fail a Load When a Gate Is Broken

A severe rule should stop publication. Run validation after staging and before the target transaction. If the violation count exceeds the agreed threshold, log the rule failures and THROW an error. SQL Agent then reports failure, and the previous published data stays in place.

Some rules are warnings. A missing optional phone number does not necessarily block a financial report. Record the warning and show it in the run summary. The gate decision should be explicit per rule, not inferred from a vague severity label alone.

What happens when the source owner disputes a rule? Keep the rule definition, sample offending rows, and run ID available. That makes the decision reviewable. I would rather have a short argument over a clear rule than a long investigation of a silent correction.

DECLARE @bad_rows bigint;
SELECT @bad_rows = COUNT_BIG(*)
FROM dbo.StageOrder
WHERE OrderAmount < 0;
IF @bad_rows > 0
    THROW 50001, 'Negative order amounts block this load.', 1;

Measure Drift in Data Quality Rules Without Inventing Success

Track violations by rule and source over time. A sudden change can reveal a source release, a format change, or a broken extraction. Do not call a load healthy solely because it crossed a threshold. Compare against the business meaning of the data and the report it feeds.

Review false positives and stale data quality rules. A rule that nobody owns will eventually be bypassed. Retire it deliberately or give it an owner. Keep a test dataset with valid, invalid, and borderline rows for each important rule. A new rule should prove that it catches the bad case without rejecting the good one.

The aim is to fail clearly before bad rows become trusted numbers. T-SQL provides the checks, but ownership provides the judgment. Together they give the next load a reason to stop and a path to resume.

Related reading on this blog: Where Should a Data Quality Check Live? Gates, Controls, and Quarantine and "Clean Data" Is Not a Requirement: Writing Rules People Can Act On.

What every rule needs: a checklist on the data quality rules

A data quality rule is not a scorecard ornament, it is an executable promise to readers.

Published by Pinal Dave on SQLAuthority. More of my work at pinaldave.com.

Data Observability, ETL, SQL Constraint and Keys, SQL Server
Previous Post
SET NOEXEC and PARSEONLY: Checking a Script Without Running It
Next Post
SQL SERVER – Finding Count of Logical CPU using T-SQL Script – Identify Virtual Processors

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.