Completeness means all the data that should be there IS there. No missing records. No blank fields. No gaps in sequences. Think of an attendance register — if 50 students are in the class but only 45 entries exist, the register is incomplete. Same logic applies to your database.
-- Comprehensive NULL report for a table
SELECT
COUNT(*) AS total_rows,
COUNT(*) - COUNT(full_name) AS missing_name,
COUNT(*) - COUNT(email) AS missing_email,
COUNT(*) - COUNT(phone) AS missing_phone,
COUNT(*) - COUNT(address) AS missing_address,
COUNT(*) - COUNT(date_of_birth) AS missing_dob
FROM users;
-- NULL percentage per column (tells you how bad it is)
SELECT
ROUND(100.0 * (COUNT(*) - COUNT(full_name)) / COUNT(*), 2)
AS name_null_pct,
ROUND(100.0 * (COUNT(*) - COUNT(email)) / COUNT(*), 2)
AS email_null_pct,
ROUND(100.0 * (COUNT(*) - COUNT(phone)) / COUNT(*), 2)
AS phone_null_pct
FROM users;
-- If email_null_pct > 0% and email is required, that is a bugAfter a data migration or bulk import, the first thing to check: did all records make it? If the source had 10,000 rows and the target has 9,847, you lost 153 records. Where did they go?
-- Basic count validation after migration
SELECT 'source' AS system, COUNT(*) AS record_count
FROM source_users
UNION ALL
SELECT 'target', COUNT(*)
FROM users;
-- Both counts should match
-- Check for gaps in expected sequences
-- Every day should have at least one transaction
SELECT d.date AS missing_date
FROM generate_series(
'2024-01-01'::date,
'2024-12-31'::date,
'1 day'::interval
) d(date)
LEFT JOIN transactions t
ON DATE(t.created_at) = d.date
WHERE t.transaction_id IS NULL
AND d.date <= CURRENT_DATE;
-- Any missing dates could indicate a system outage or data loss
-- Every user who placed an order should have a shipping address
SELECT o.order_id, o.user_id
FROM orders o
LEFT JOIN addresses a ON o.user_id = a.user_id
WHERE a.address_id IS NULL
AND o.status = 'shipped';-- Every active product should have at least one image
SELECT p.product_id, p.name
FROM products p
LEFT JOIN product_images pi ON p.product_id = pi.product_id
WHERE pi.image_id IS NULL
AND p.is_active = true;
-- Every employee should have an assigned department
SELECT employee_id, full_name
FROM employees
WHERE department_id IS NULL;
-- Every completed order should have a payment record
SELECT o.order_id, o.user_id, o.total_amount
FROM orders o
LEFT JOIN payments p ON o.order_id = p.order_id
WHERE p.payment_id IS NULL
AND o.status = 'completed';Run NULL analysis queries as part of your daily smoke test. Set up alerts if the NULL percentage on critical columns crosses 1%. This catches data quality problems before they snowball.
Key Point: Data completeness checks answer one question: Is everything that should be here actually here? Check NULL counts, record counts, sequence gaps, and coverage of related data.
Key Point: Data completeness means no missing records, no unexpected NULLs, and no gaps. Validate with NULL analysis, count comparisons, and coverage queries.