> ## 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 queries

# 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

```sql theme={null}
-- 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

```sql theme={null}
-- 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

```sql theme={null}
-- 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

```sql theme={null}
-- 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

```sql theme={null}
-- 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

```sql theme={null}
-- 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

```sql theme={null}
-- 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

```sql theme={null}
-- 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

```sql theme={null}
-- 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

```sql theme={null}
-- 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

```sql theme={null}
-- 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

```sql theme={null}
-- 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

```sql theme={null}
-- 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

```sql theme={null}
-- 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

```sql theme={null}
-- 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

```sql theme={null}
-- 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

```sql theme={null}
-- 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](/planning-center/registrations/advanced-queries) guide for CTEs, window functions, and cross-module integration.
