Documentation Index
Fetch the complete documentation index at: https://docs.getparable.io/llms.txt
Use this file to discover all available pages before exploring further.
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;
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;
-- 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;
-- 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
- Start simple - Begin with single table queries and gradually add joins
- Use DISTINCT carefully - Only when you need unique values
- Filter early - Add WHERE clauses before GROUP BY for better performance
- Test with LIMIT - Add LIMIT 10 when testing queries on large datasets
- Check for NULLs - Many fields can be NULL, use COALESCE or IS NULL checks
- 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.