Skip to main content

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.