Knowledge > Runbooks > Customer Ops > Merge Duplicates
Merge Duplicate Church Records
Consolidate two duplicate church entries in the churches table into a single canonical record.
Prerequisites
- Supabase access
- Explicit founder approval — this is a manual data operation affecting public directory data
- Both church UUIDs identified
CRITICAL Rules
- Never DELETE either church row — set
directory_visible = falseon the duplicate. - Always keep the row with the most complete data and/or premium subscription as the canonical.
- Foreign key safety: multiple tables reference
church_id. Update them all before hiding the duplicate. - Test every query with SELECT first.
Steps
1. Identify both records and choose the canonical one
SELECT id, name, slug, email, address, city, state, phone, website,
directory_visible, created_at
FROM churches
WHERE name ILIKE '%[church-name]%'
OR slug IN ('[slug1]', '[slug2]')
ORDER BY created_at;
Choose the canonical record (the one to keep) based on:
- Has a premium subscription (
premium_churchesrow) - Was claimed first (
church_admin_sessionswith earliercreated_at) - Has more complete / accurate data
- Has the most recognizable slug
Note: canonical UUID = keep this one. duplicate UUID = will be hidden.
2. Copy missing data from duplicate to canonical
Review each field on both records. For fields that are better on the duplicate:
UPDATE churches
SET
phone = '[value-from-duplicate]', -- example
website = '[value-from-duplicate]', -- example
updated_at = now()
WHERE id = '[canonical-uuid]';
Do this field by field. Don't overwrite good data with worse data.
3. Update premium_churches if the duplicate has the subscription
-- Check which record has the premium subscription
SELECT church_id, plan, tier, stripe_subscription_id
FROM premium_churches
WHERE church_id IN ('[canonical-uuid]', '[duplicate-uuid]');
If the duplicate has the subscription and the canonical does not:
UPDATE premium_churches
SET church_id = '[canonical-uuid]',
updated_at = now()
WHERE church_id = '[duplicate-uuid]';
4. Update church_admin_sessions if the duplicate has sessions
SELECT church_id, token, created_at FROM church_admin_sessions
WHERE church_id IN ('[canonical-uuid]', '[duplicate-uuid]');
If the duplicate has sessions that should transfer to the canonical:
UPDATE church_admin_sessions
SET church_id = '[canonical-uuid]'
WHERE church_id = '[duplicate-uuid]';
5. Update church_voice_agents if applicable
SELECT church_id FROM church_voice_agents
WHERE church_id = '[duplicate-uuid]';
If exists, update to canonical:
UPDATE church_voice_agents
SET church_id = '[canonical-uuid]'
WHERE church_id = '[duplicate-uuid]';
6. Update organization_settings if applicable
UPDATE organization_settings
SET church_id = '[canonical-uuid]'
WHERE church_id = '[duplicate-uuid]';
7. Check for any other foreign key references
-- Find all tables referencing church_id
SELECT table_name FROM information_schema.columns
WHERE column_name = 'church_id'
ORDER BY table_name;
Review each table and update if the duplicate UUID is referenced.
8. Hide the duplicate
UPDATE churches
SET directory_visible = false,
updated_at = now()
WHERE id = '[duplicate-uuid]';
9. Verify
-- Canonical should have all data, directory_visible = true
SELECT id, name, slug, directory_visible, updated_at FROM churches WHERE id = '[canonical-uuid]';
-- Duplicate should be hidden
SELECT id, name, directory_visible FROM churches WHERE id = '[duplicate-uuid]';
-- Premium subscription should point to canonical
SELECT church_id, plan FROM premium_churches WHERE church_id IN ('[canonical-uuid]', '[duplicate-uuid]');
Verification
On PewSearch, the canonical record should appear in search results. The duplicate listing should no longer appear. If the admin previously used the duplicate's token, generate a new session for the canonical UUID.
See Also
- remove-church.md — hiding a listing
- update-church-info.md — updating church data
- claim-dispute.md — if the duplicate was claimed by a different person