This guide provides simple, ready-to-use SQL queries for Planning Center Check-ins data. Each query is designed to answer common ministry questions without requiring deep SQL knowledge.
Query Customization Required
These example queries demonstrate common patterns but may require adjustments to match your specific database schema and field names. Test thoroughly in your environment before use.
IMPORTANT: All tables in the Planning Center Check-ins module live in the planning_center schema. Always prefix table names with planning_center. when writing queries.✅ CORRECT: SELECT * FROM planning_center.checkins_checkins
❌ INCORRECT: SELECT * FROM checkins_checkins
-- List everyone who checked in todaySELECT first_name, last_name, kind as check_in_type, security_code, created_at as checked_in_at, CASE WHEN checked_out_at IS NULL THEN 'Still here' ELSE 'Checked out' END as statusFROM planning_center.checkins_checkinsWHERE DATE(created_at) = CURRENT_DATEORDER BY created_at DESC;
-- See how many regulars, guests, and volunteers checked in todaySELECT kind as attendee_type, COUNT(*) as totalFROM planning_center.checkins_checkinsWHERE DATE(created_at) = CURRENT_DATEGROUP BY kindORDER BY total DESC;
-- Find who is still checked in right nowSELECT first_name, last_name, security_code, kind as type, created_at as checked_in_at, EXTRACT(HOUR FROM AGE(NOW(), created_at)) as hours_checked_inFROM planning_center.checkins_checkinsWHERE DATE(created_at) = CURRENT_DATE AND checked_out_at IS NULLORDER BY created_at;
-- Count check-ins for each location todaySELECT l.name as location_name, COUNT(c.check_in_id) as total_checkinsFROM planning_center.checkins_locations lLEFT JOIN planning_center.checkins_checkin_relationships cr ON l.location_id = cr.relationship_id AND cr.relationship_type = 'Location'LEFT JOIN planning_center.checkins_checkins c ON cr.checkin_id = c.check_in_id AND DATE(c.created_at) = CURRENT_DATEGROUP BY l.nameHAVING COUNT(c.check_in_id) > 0ORDER BY total_checkins DESC;
-- Check how full each room isSELECT l.name as room, l.max_occupancy as capacity, COUNT(c.check_in_id) as current_count, CASE WHEN l.max_occupancy IS NULL THEN 'No limit set' WHEN COUNT(c.check_in_id) >= l.max_occupancy THEN 'FULL' WHEN COUNT(c.check_in_id) >= l.max_occupancy * 0.8 THEN 'Nearly full' ELSE 'Space available' END as statusFROM planning_center.checkins_locations lLEFT JOIN planning_center.checkins_checkin_relationships cr ON l.location_id = cr.relationship_id AND cr.relationship_type = 'Location'LEFT JOIN planning_center.checkins_checkins c ON cr.checkin_id = c.check_in_id AND DATE(c.created_at) = CURRENT_DATE AND c.checked_out_at IS NULL -- Only currently presentWHERE l.kind = 'Folder' -- Physical locations onlyGROUP BY l.name, l.max_occupancyORDER BY l.name;
-- Find all first-time guests from the past 7 daysSELECT first_name, last_name, DATE(created_at) as visit_date, security_codeFROM planning_center.checkins_checkinsWHERE one_time_guest = true AND created_at >= CURRENT_DATE - INTERVAL '7 days'ORDER BY created_at DESC;
-- Compare guest and regular attendance by day this monthSELECT DATE(created_at) as date, COUNT(CASE WHEN kind = 'Guest' THEN 1 END) as guests, COUNT(CASE WHEN kind = 'Regular' THEN 1 END) as regulars, COUNT(*) as totalFROM planning_center.checkins_checkinsWHERE created_at >= DATE_TRUNC('month', CURRENT_DATE)GROUP BY DATE(created_at)ORDER BY date DESC;
-- List all volunteers who checked in todaySELECT first_name, last_name, created_at as checked_in_at, CASE WHEN checked_out_at IS NULL THEN 'Currently serving' ELSE 'Finished serving' END as statusFROM planning_center.checkins_checkinsWHERE kind = 'Volunteer' AND DATE(created_at) = CURRENT_DATEORDER BY created_at;
-- See volunteer coverage throughout the daySELECT DATE_TRUNC('hour', created_at) as hour, COUNT(*) as volunteers_checked_inFROM planning_center.checkins_checkinsWHERE kind = 'Volunteer' AND DATE(created_at) = CURRENT_DATEGROUP BY DATE_TRUNC('hour', created_at)ORDER BY hour;
-- See which days have the highest attendanceSELECT TO_CHAR(created_at, 'Day') as day_of_week, COUNT(*) as total_checkins, COUNT(DISTINCT DATE(created_at)) as number_of_days, ROUND(COUNT(*)::NUMERIC / COUNT(DISTINCT DATE(created_at))) as avg_per_dayFROM planning_center.checkins_checkinsWHERE created_at >= CURRENT_DATE - INTERVAL '30 days'GROUP BY TO_CHAR(created_at, 'Day'), EXTRACT(DOW FROM created_at)ORDER BY EXTRACT(DOW FROM created_at);
-- Find when most people check inSELECT TO_CHAR(created_at, 'HH:00 AM') as hour, COUNT(*) as checkinsFROM planning_center.checkins_checkinsWHERE created_at >= CURRENT_DATE - INTERVAL '7 days'GROUP BY TO_CHAR(created_at, 'HH:00 AM'), EXTRACT(HOUR FROM created_at)ORDER BY EXTRACT(HOUR FROM created_at);
-- Show events and their attendance countsSELECT e.name as event_name, COUNT(DISTINCT c.check_in_id) as total_attendanceFROM planning_center.checkins_events eLEFT JOIN planning_center.checkins_checkin_relationships cr ON e.event_id = cr.relationship_id AND cr.relationship_type = 'Event'LEFT JOIN planning_center.checkins_checkins c ON cr.checkin_id = c.check_in_id AND c.created_at >= CURRENT_DATE - INTERVAL '7 days'GROUP BY e.nameHAVING COUNT(DISTINCT c.check_in_id) > 0ORDER BY total_attendance DESC;
-- See how often events are scheduledSELECT name as event, frequency, CASE WHEN archived_at IS NOT NULL THEN 'Archived' ELSE 'Active' END as statusFROM planning_center.checkins_eventsORDER BY name;
-- List all security codes currently in useSELECT DISTINCT security_code, COUNT(*) as times_usedFROM planning_center.checkins_checkinsWHERE DATE(created_at) = CURRENT_DATE AND checked_out_at IS NULL AND security_code IS NOT NULLGROUP BY security_codeORDER BY security_code;
-- Find check-ins with emergency contactsSELECT first_name, last_name, emergency_contact_name, emergency_contact_phone_number, security_code, created_atFROM planning_center.checkins_checkinsWHERE DATE(created_at) = CURRENT_DATE AND emergency_contact_name IS NOT NULLORDER BY created_at DESC;
-- Find children with medical notesSELECT first_name, last_name, medical_notes, security_code, created_at as checked_in_atFROM planning_center.checkins_checkinsWHERE medical_notes IS NOT NULL AND medical_notes != '' AND DATE(created_at) = CURRENT_DATEORDER BY created_at DESC;
-- Calculate how long people typically staySELECT kind as attendee_type, COUNT(*) as total_checkouts, ROUND(AVG(EXTRACT(EPOCH FROM (checked_out_at - created_at))/3600), 1) as avg_hoursFROM planning_center.checkins_checkinsWHERE checked_out_at IS NOT NULL AND created_at >= CURRENT_DATE - INTERVAL '30 days'GROUP BY kindORDER BY avg_hours DESC;
-- Find the days with the most check-insSELECT DATE(created_at) as date, TO_CHAR(created_at, 'Day') as day_name, COUNT(*) as total_checkinsFROM planning_center.checkins_checkinsWHERE created_at >= CURRENT_DATE - INTERVAL '3 months'GROUP BY DATE(created_at), TO_CHAR(created_at, 'Day')ORDER BY total_checkins DESCLIMIT 10;