Basic Registrations Queries

Simple, Practical SQL for Event Management

Start here to learn the fundamentals of querying Planning Center Registrations data. These examples cover common scenarios you’ll encounter in day-to-day event management.

Query Requirements

Schema Prefix

IMPORTANT: All tables in the Planning Center Registrations module live in the planning_center schema. Always prefix table names with planning_center. in your queries. ✅ CORRECT: SELECT * FROM planning_center.registrations_signups ❌ INCORRECT: SELECT * FROM registrations_signups

Row Level Security (RLS)

Row Level Security automatically filters results by:
  • tenant_organization_id – limits data to your organization
  • system_status – returns active records by default
Skip manual filters for these columns—RLS already enforces them and redundant predicates can mask data or slow execution:
  • WHERE tenant_organization_id = 1
  • WHERE system_status = 'active'
Keep your focus on event-specific filters (archived flags, dates, statuses) while RLS handles tenancy and system status automatically.

Finding Events

List All Active Events

-- Get all non-archived events
SELECT 
    signup_id,
    name,
    description,
    open_at,
    close_at,
    created_at
FROM planning_center.registrations_signups
WHERE archived = false
ORDER BY created_at DESC;

Find Upcoming Events

-- Events with future dates
SELECT 
    s.name as event_name,
    st.starts_at as event_date,
    st.ends_at,
    st.all_day
FROM planning_center.registrations_signups s
JOIN planning_center.registrations_signup_relationships sr 
    ON sr.signup_id = s.signup_id 
    AND sr.relationship_type = 'signup_time'
JOIN planning_center.registrations_signup_times st 
    ON st.signup_time_id = sr.relationship_id
WHERE s.archived = false
  AND st.starts_at >= CURRENT_DATE
ORDER BY st.starts_at;

Events by Category

-- Find events in specific categories
SELECT 
    s.name as event_name,
    c.name as category_name
FROM planning_center.registrations_signups s
JOIN planning_center.registrations_signup_relationships sr 
    ON sr.signup_id = s.signup_id 
    AND sr.relationship_type = 'category'
JOIN planning_center.registrations_categories c 
    ON c.category_id = sr.relationship_id
WHERE s.archived = false
ORDER BY c.name, s.name;

Registration Counts

Count Attendees per Event

-- Basic registration counts
SELECT 
    s.name as event_name,
    COUNT(DISTINCT a.attendee_id) as total_attendees,
    COUNT(CASE WHEN a.active = true THEN 1 END) as active_registrations,
    COUNT(CASE WHEN a.canceled = true THEN 1 END) as canceled_registrations,
    COUNT(CASE WHEN a.waitlisted = true THEN 1 END) as waitlisted
FROM planning_center.registrations_signups s
LEFT JOIN planning_center.registrations_attendee_relationships ar 
    ON ar.relationship_type = 'signup' 
    AND ar.relationship_id = s.signup_id
LEFT JOIN planning_center.registrations_attendees a 
    ON a.attendee_id = ar.attendee_id
WHERE s.archived = false
GROUP BY s.name
ORDER BY total_attendees DESC;

Recent Registrations

-- Last 20 registrations across all events
SELECT 
    p.name as attendee_name,
    s.name as event_name,
    a.created_at as registration_date,
    CASE 
        WHEN a.waitlisted = true THEN 'Waitlisted'
        WHEN a.canceled = true THEN 'Canceled'
        WHEN a.active = true THEN 'Active'
        ELSE 'Unknown'
    END as status
FROM planning_center.registrations_attendees a
JOIN planning_center.registrations_attendee_relationships ar_signup 
    ON ar_signup.attendee_id = a.attendee_id 
    AND ar_signup.relationship_type = 'signup'
JOIN planning_center.registrations_signups s 
    ON s.signup_id = ar_signup.relationship_id
JOIN planning_center.registrations_attendee_relationships ar_reg 
    ON ar_reg.attendee_id = a.attendee_id 
    AND ar_reg.relationship_type = 'registration'
JOIN planning_center.registrations_registration_relationships rr 
    ON rr.registration_id = ar_reg.relationship_id 
    AND rr.relationship_type = 'person'
JOIN planning_center.registrations_people p 
    ON p.person_id = rr.relationship_id
ORDER BY a.created_at DESC
LIMIT 20;

Waitlist Management

View Waitlisted Attendees

-- All waitlisted attendees with their wait times
SELECT 
    p.name as attendee_name,
    s.name as event_name,
    a.waitlisted_at,
    CURRENT_TIMESTAMP - a.waitlisted_at as time_on_waitlist
FROM planning_center.registrations_attendees a
JOIN planning_center.registrations_attendee_relationships ar_signup 
    ON ar_signup.attendee_id = a.attendee_id 
    AND ar_signup.relationship_type = 'signup'
JOIN planning_center.registrations_signups s 
    ON s.signup_id = ar_signup.relationship_id
JOIN planning_center.registrations_attendee_relationships ar_reg 
    ON ar_reg.attendee_id = a.attendee_id 
    AND ar_reg.relationship_type = 'registration'
JOIN planning_center.registrations_registration_relationships rr 
    ON rr.registration_id = ar_reg.relationship_id 
    AND rr.relationship_type = 'person'
JOIN planning_center.registrations_people p 
    ON p.person_id = rr.relationship_id
WHERE a.waitlisted = true
ORDER BY s.name, a.waitlisted_at;

Events with Waitlists

-- Find events that have waitlists
SELECT 
    s.name as event_name,
    COUNT(CASE WHEN a.active = true THEN 1 END) as active_count,
    COUNT(CASE WHEN a.waitlisted = true THEN 1 END) as waitlist_count
FROM planning_center.registrations_signups s
LEFT JOIN planning_center.registrations_attendee_relationships ar 
    ON ar.relationship_type = 'signup' 
    AND ar.relationship_id = s.signup_id
LEFT JOIN planning_center.registrations_attendees a 
    ON a.attendee_id = ar.attendee_id
WHERE s.archived = false
GROUP BY s.name
HAVING COUNT(CASE WHEN a.waitlisted = true THEN 1 END) > 0
ORDER BY waitlist_count DESC;

Location Information

Events by Location

-- List events with their venues
SELECT 
    s.name as event_name,
    sl.name as location_name,
    sl.formatted_address,
    sl.latitude,
    sl.longitude
FROM planning_center.registrations_signups s
JOIN planning_center.registrations_signup_relationships sr 
    ON sr.signup_id = s.signup_id 
    AND sr.relationship_type = 'signup_location'
JOIN planning_center.registrations_signup_locations sl 
    ON sl.signup_location_id = sr.relationship_id
WHERE s.archived = false
ORDER BY sl.name, s.name;

Campus Events

-- Events by campus
SELECT 
    c.name as campus_name,
    s.name as event_name,
    st.starts_at as event_date
FROM planning_center.registrations_signups s
JOIN planning_center.registrations_signup_relationships sr_campus 
    ON sr_campus.signup_id = s.signup_id 
    AND sr_campus.relationship_type = 'campus'
JOIN planning_center.registrations_campuses c 
    ON c.campus_id = sr_campus.relationship_id
LEFT JOIN planning_center.registrations_signup_relationships sr_time 
    ON sr_time.signup_id = s.signup_id 
    AND sr_time.relationship_type = 'signup_time'
LEFT JOIN planning_center.registrations_signup_times st 
    ON st.signup_time_id = sr_time.relationship_id
WHERE s.archived = false
ORDER BY c.name, st.starts_at;

Pricing and Selection Types

View Event Pricing Options

-- Get pricing tiers for events
SELECT 
    s.name as event_name,
    selt.name as ticket_type,
    selt.price_cents / 100.0 as price_dollars,
    selt.publicly_available
FROM planning_center.registrations_signups s
JOIN planning_center.registrations_selection_types selt 
    ON EXISTS (
        SELECT 1 
        FROM planning_center.registrations_signup_relationships sr
        WHERE sr.signup_id = s.signup_id
        AND sr.relationship_type = 'selection_type'
        AND sr.relationship_id = selt.selection_type_id
    )
WHERE s.archived = false
ORDER BY s.name, selt.price_cents;

Calculate Event Revenue Potential

-- Simple revenue calculation by selection type
SELECT 
    s.name as event_name,
    selt.name as ticket_type,
    selt.price_cents / 100.0 as price_dollars,
    COUNT(a.attendee_id) as tickets_sold,
    (selt.price_cents * COUNT(a.attendee_id)) / 100.0 as total_revenue
FROM planning_center.registrations_signups s
JOIN planning_center.registrations_selection_types selt 
    ON EXISTS (
        SELECT 1 
        FROM planning_center.registrations_signup_relationships sr
        WHERE sr.signup_id = s.signup_id
        AND sr.relationship_type = 'selection_type'
        AND sr.relationship_id = selt.selection_type_id
    )
LEFT JOIN planning_center.registrations_attendee_relationships ar 
    ON ar.relationship_type = 'signup' 
    AND ar.relationship_id = s.signup_id
LEFT JOIN planning_center.registrations_attendees a 
    ON a.attendee_id = ar.attendee_id 
    AND a.active = true
WHERE s.archived = false
GROUP BY s.name, selt.name, selt.price_cents
ORDER BY s.name, selt.price_cents;

Emergency Contacts

List Emergency Contacts for Event

-- Get emergency contacts for active attendees
SELECT 
    p.name as attendee_name,
    s.name as event_name,
    ec.name as emergency_contact_name,
    ec.phone_number as emergency_phone
FROM planning_center.registrations_attendees a
-- Link to signup
JOIN planning_center.registrations_attendee_relationships ar_signup 
    ON ar_signup.attendee_id = a.attendee_id 
    AND ar_signup.relationship_type = 'signup'
JOIN planning_center.registrations_signups s 
    ON s.signup_id = ar_signup.relationship_id
-- Link to person
JOIN planning_center.registrations_attendee_relationships ar_reg 
    ON ar_reg.attendee_id = a.attendee_id 
    AND ar_reg.relationship_type = 'registration'
JOIN planning_center.registrations_registration_relationships rr 
    ON rr.registration_id = ar_reg.relationship_id 
    AND rr.relationship_type = 'person'
JOIN planning_center.registrations_people p 
    ON p.person_id = rr.relationship_id
-- Link to emergency contact
LEFT JOIN planning_center.registrations_attendee_relationships ar_ec 
    ON ar_ec.attendee_id = a.attendee_id 
    AND ar_ec.relationship_type = 'emergency_contact'
LEFT JOIN planning_center.registrations_emergency_contacts ec 
    ON ec.emergency_contact_id = ar_ec.relationship_id
WHERE a.active = true
  AND s.archived = false
ORDER BY s.name, p.name;

Check Emergency Contact Coverage

-- Find attendees without emergency contacts
SELECT 
    s.name as event_name,
    COUNT(DISTINCT a.attendee_id) as total_attendees,
    COUNT(DISTINCT ar_ec.attendee_id) as with_emergency_contact,
    COUNT(DISTINCT a.attendee_id) - COUNT(DISTINCT ar_ec.attendee_id) as missing_emergency_contact
FROM planning_center.registrations_signups s
JOIN planning_center.registrations_attendee_relationships ar_signup 
    ON ar_signup.relationship_type = 'signup' 
    AND ar_signup.relationship_id = s.signup_id
JOIN planning_center.registrations_attendees a 
    ON a.attendee_id = ar_signup.attendee_id 
    AND a.active = true
LEFT JOIN planning_center.registrations_attendee_relationships ar_ec 
    ON ar_ec.attendee_id = a.attendee_id 
    AND ar_ec.relationship_type = 'emergency_contact'
WHERE s.archived = false
GROUP BY s.name
ORDER BY missing_emergency_contact DESC;

Date and Time Queries

Events This Month

-- All events happening this month
SELECT 
    s.name as event_name,
    st.starts_at,
    st.ends_at,
    st.all_day
FROM planning_center.registrations_signups s
JOIN planning_center.registrations_signup_relationships sr 
    ON sr.signup_id = s.signup_id 
    AND sr.relationship_type = 'signup_time'
JOIN planning_center.registrations_signup_times st 
    ON st.signup_time_id = sr.relationship_id
WHERE s.archived = false
  AND DATE_PART('month', st.starts_at) = DATE_PART('month', CURRENT_DATE)
  AND DATE_PART('year', st.starts_at) = DATE_PART('year', CURRENT_DATE)
ORDER BY st.starts_at;

Registration Windows

-- Events with open/closed registration periods
SELECT 
    name as event_name,
    open_at,
    close_at,
    CASE 
        WHEN CURRENT_TIMESTAMP < open_at THEN 'Not Yet Open'
        WHEN CURRENT_TIMESTAMP BETWEEN open_at AND close_at THEN 'Open'
        WHEN CURRENT_TIMESTAMP > close_at THEN 'Closed'
        ELSE 'Always Open'
    END as registration_status
FROM planning_center.registrations_signups
WHERE archived = false
ORDER BY 
    CASE 
        WHEN CURRENT_TIMESTAMP BETWEEN open_at AND close_at THEN 1
        WHEN CURRENT_TIMESTAMP < open_at THEN 2
        ELSE 3
    END,
    open_at;

People and Attendees

Find Person’s Registrations

-- All events a person is registered for
SELECT 
    p.name as person_name,
    s.name as event_name,
    a.created_at as registered_at,
    CASE 
        WHEN a.waitlisted = true THEN 'Waitlisted'
        WHEN a.canceled = true THEN 'Canceled'
        WHEN a.active = true THEN 'Active'
        ELSE 'Unknown'
    END as status
FROM planning_center.registrations_people p
JOIN planning_center.registrations_registration_relationships rr 
    ON rr.relationship_id = p.person_id 
    AND rr.relationship_type = 'person'
JOIN planning_center.registrations_attendee_relationships ar 
    ON ar.relationship_id = rr.registration_id 
    AND ar.relationship_type = 'registration'
JOIN planning_center.registrations_attendees a 
    ON a.attendee_id = ar.attendee_id
JOIN planning_center.registrations_attendee_relationships ar_signup 
    ON ar_signup.attendee_id = a.attendee_id 
    AND ar_signup.relationship_type = 'signup'
JOIN planning_center.registrations_signups s 
    ON s.signup_id = ar_signup.relationship_id
WHERE p.name ILIKE '%smith%'  -- Search by name
ORDER BY a.created_at DESC;

Most Active Participants

-- People registered for the most events
SELECT 
    p.name as person_name,
    COUNT(DISTINCT s.signup_id) as events_registered,
    COUNT(CASE WHEN a.active = true THEN 1 END) as active_registrations
FROM planning_center.registrations_people p
JOIN planning_center.registrations_registration_relationships rr 
    ON rr.relationship_id = p.person_id 
    AND rr.relationship_type = 'person'
JOIN planning_center.registrations_attendee_relationships ar 
    ON ar.relationship_id = rr.registration_id 
    AND ar.relationship_type = 'registration'
JOIN planning_center.registrations_attendees a 
    ON a.attendee_id = ar.attendee_id
JOIN planning_center.registrations_attendee_relationships ar_signup 
    ON ar_signup.attendee_id = a.attendee_id 
    AND ar_signup.relationship_type = 'signup'
JOIN planning_center.registrations_signups s 
    ON s.signup_id = ar_signup.relationship_id
GROUP BY p.name
HAVING COUNT(DISTINCT s.signup_id) > 1
ORDER BY events_registered DESC;

Tips for Basic Queries

  1. Start simple - Begin with single table queries and gradually add joins
  2. Use DISTINCT carefully - Only when you need unique values
  3. Filter early - Add WHERE clauses before GROUP BY for better performance
  4. Test with LIMIT - Add LIMIT 10 when testing queries on large datasets
  5. Check for NULLs - Many fields can be NULL, use COALESCE or IS NULL checks
  6. Understand relationships - Always join through the relationship tables

Next Steps

Ready for more complex analysis? Check out our Advanced Queries guide for CTEs, window functions, and cross-module integration.