Answer
Data Integrity Validation Queries
Data integrity checks ensure relationships and constraints are maintained across tables.
1. Find Orphan Records (Foreign Key Violations)
SQL
-- Find order_items with no matching order (orphans)
SELECT oi.*
FROM order_items oi
LEFT JOIN orders o ON oi.order_id = o.id
WHERE o.id IS NULL;
-- Expected: 0 rows (no orphan items)
-- Find orders with no matching customer
SELECT o.id, o.customer_id, o.total_amount
FROM orders o
LEFT JOIN customers c ON o.customer_id = c.id
WHERE c.id IS NULL;
-- Expected: 0 rows
-- Find employees assigned to non-existent departments
SELECT e.id, e.name, e.dept_id
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.id
WHERE d.id IS NULL AND e.dept_id IS NOT NULL;
-- Expected: 0 rows
2. Verify Row Counts Match (Source vs Target)
SQL
-- After data migration: source vs target count
SELECT
(SELECT COUNT(*) FROM source_customers) AS source_count,
(SELECT COUNT(*) FROM target_customers) AS target_count,
(SELECT COUNT(*) FROM source_customers) -
(SELECT COUNT(*) FROM target_customers) AS difference;
-- Expected: difference = 0
-- Per-status count comparison
SELECT
status,
COUNT(*) AS source_count
FROM source_orders
GROUP BY status
EXCEPT -- or MINUS in Oracle
SELECT
status,
COUNT(*) AS target_count
FROM target_orders
GROUP BY status;
-- Expected: 0 rows (both have same status distribution)
3. Find Records Missing in Target (Data Loss Check)
SQL
-- Rows in source but NOT in target after migration
SELECT id, email FROM source_users
WHERE id NOT IN (SELECT id FROM target_users);
-- Or using EXCEPT (more efficient)
SELECT id, email FROM source_users
EXCEPT
SELECT id, email FROM target_users;
-- Expected: 0 rows
-- Records in target but not in source (phantom records)
SELECT id FROM target_users
EXCEPT
SELECT id FROM source_users;
-- Expected: 0 rows
4. Verify Data Values Match Between Tables
SQL
-- Check users table matches between source and target
SELECT s.id, s.email AS source_email, t.email AS target_email,
s.name AS source_name, t.name AS target_name
FROM source_users s
JOIN target_users t ON s.id = t.id
WHERE s.email != t.email
OR s.name != t.name
OR COALESCE(s.phone, '') != COALESCE(t.phone, '');
-- Expected: 0 rows (no mismatches)
5. Check Financial/Calculation Integrity
SQL
-- Verify order total = sum of line items
SELECT o.id, o.total_amount,
SUM(oi.quantity * oi.unit_price) AS calculated_total,
o.total_amount - SUM(oi.quantity * oi.unit_price) AS discrepancy
FROM orders o
JOIN order_items oi ON oi.order_id = o.id
GROUP BY o.id, o.total_amount
HAVING ABS(o.total_amount - SUM(oi.quantity * oi.unit_price)) > 0.01;
-- Expected: 0 rows (no calculation errors)
-- Verify payment amounts match order amounts
SELECT o.id, o.total_amount, p.amount AS paid_amount
FROM orders o
JOIN payments p ON p.order_id = o.id
WHERE o.total_amount != p.amount
AND o.status = 'paid';
-- Expected: 0 rows
6. Validate Uniqueness Constraints
SQL
-- Ensure no duplicate primary keys (shouldn't happen but good to check)
SELECT id, COUNT(*) FROM users GROUP BY id HAVING COUNT(*) > 1;
-- Ensure email is unique
SELECT email, COUNT(*) AS cnt
FROM users
GROUP BY email
HAVING COUNT(*) > 1;
-- Expected: 0 rows
-- Ensure unique order numbers
SELECT order_number, COUNT(*)
FROM orders
GROUP BY order_number
HAVING COUNT(*) > 1;
7. Date/Sequence Integrity
SQL
-- Find orders where created_at > updated_at (impossible)
SELECT id, created_at, updated_at
FROM orders
WHERE updated_at < created_at;
-- Expected: 0 rows
-- Find cancelled orders that still have active payments
SELECT o.id AS order_id, p.id AS payment_id
FROM orders o
JOIN payments p ON p.order_id = o.id
WHERE o.status = 'cancelled'
AND p.status = 'active';
-- Expected: 0 rows (business rule violation)
