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 Publishing Queries
Simple, powerful queries to get started with your Publishing data. Copy, paste, and customize these for your needs!
Query Requirements
Schema Prefix
IMPORTANT: All tables in the Planning Center Publishing module live in the planning_center schema. Always prefix table names with planning_center. when querying.
✅ CORRECT: SELECT * FROM planning_center.publishing_episodes
❌ INCORRECT: SELECT * FROM publishing_episodes
Row Level Security (RLS)
Row Level Security automatically governs:
- tenant_organization_id – restricts results to your organization
- system_status – returns active records by default
Do not add these filters manually—RLS already applies them and redundant predicates can suppress data or hurt performance:
- ❌
WHERE tenant_organization_id = 1
- ❌
WHERE system_status = 'active'
Focus on content-specific filters (series, published dates, media metadata) while relying on RLS for tenancy and status.
Recent Episodes
Latest Published Episodes
See your most recent content with key details.
-- Get the 20 most recent published episodes
SELECT
episode_id,
title,
description,
published_live_at,
video_url,
church_center_url
FROM planning_center.publishing_episodes
WHERE published_live_at IS NOT NULL
ORDER BY published_live_at DESC
LIMIT 20;
Episodes by Series
Find all episodes in a specific series.
-- Get all episodes from a series (replace series name)
SELECT
e.title as episode_title,
e.published_live_at,
e.description,
s.title as series_title,
s.started_at as series_start,
s.ended_at as series_end
FROM planning_center.publishing_episodes e
JOIN planning_center.publishing_series s
ON e.series_id = s.series_id
WHERE s.title LIKE '%Christmas%' -- Change series name here
ORDER BY e.published_live_at;
Episodes This Year
All episodes published in the current year.
-- Episodes published this year
SELECT
title,
published_live_at,
video_url,
DATE_PART('month', published_live_at) as month,
DATE_PART('week', published_live_at) as week
FROM planning_center.publishing_episodes
WHERE published_live_at >= DATE_TRUNC('year', CURRENT_DATE)
AND published_live_at IS NOT NULL
ORDER BY published_live_at DESC;
Series Analytics
Active Series List
All series with episode counts.
-- List all series with episode counts
SELECT
s.series_id,
s.title,
s.description,
s.started_at,
s.ended_at,
s.episodes_count,
s.published,
COUNT(e.episode_id) as actual_episode_count
FROM planning_center.publishing_series s
LEFT JOIN planning_center.publishing_episodes e
ON s.series_id = e.series_id
GROUP BY s.series_id, s.title, s.description,
s.started_at, s.ended_at, s.episodes_count, s.published
ORDER BY s.started_at DESC;
Current/Recent Series
Series that are currently active or recently ended.
-- Current and recent series (last 6 months)
SELECT
title,
description,
started_at,
ended_at,
episodes_count,
CASE
WHEN ended_at IS NULL THEN 'Ongoing'
WHEN ended_at > CURRENT_DATE THEN 'Upcoming'
ELSE 'Completed'
END as status
FROM planning_center.publishing_series
WHERE started_at >= CURRENT_DATE - INTERVAL '6 months'
OR ended_at IS NULL
OR ended_at >= CURRENT_DATE - INTERVAL '1 month'
ORDER BY started_at DESC;
Series Duration Analysis
How long do your series typically run?
-- Average series length and episode count
SELECT
COUNT(*) as total_series,
AVG(episodes_count) as avg_episodes_per_series,
AVG(
CASE
WHEN ended_at IS NOT NULL
THEN EXTRACT(DAY FROM (ended_at - started_at))
ELSE NULL
END
) as avg_series_duration_days,
MAX(episodes_count) as max_episodes_in_series,
MIN(episodes_count) as min_episodes_in_series
FROM planning_center.publishing_series
WHERE published = true
AND started_at IS NOT NULL;
Speaker Insights
Speaker Frequency
How often does each speaker teach?
-- Speaker frequency in the last year
SELECT
sp.speaker_id,
sp.name as speaker_name,
COUNT(DISTINCT e.episode_id) as episode_count,
MIN(e.published_live_at) as first_episode,
MAX(e.published_live_at) as latest_episode,
COUNT(DISTINCT e.series_id) as series_count
FROM planning_center.publishing_speakers sp
JOIN planning_center.publishing_speakerships ship
ON sp.speaker_id = ship.speaker_id
JOIN planning_center.publishing_episodes e
ON ship.episode_id = e.episode_id
WHERE e.published_live_at >= CURRENT_DATE - INTERVAL '1 year'
GROUP BY sp.speaker_id, sp.name
ORDER BY episode_count DESC;
Recent Speakers
Who has spoken in the last few months?
-- Speakers in the last 3 months
SELECT DISTINCT
sp.name as speaker_name,
sp.description,
COUNT(e.episode_id) as recent_episodes,
MAX(e.published_live_at) as most_recent_episode
FROM planning_center.publishing_speakers sp
JOIN planning_center.publishing_speakerships ship
ON sp.speaker_id = ship.speaker_id
JOIN planning_center.publishing_episodes e
ON ship.episode_id = e.episode_id
WHERE e.published_live_at >= CURRENT_DATE - INTERVAL '3 months'
GROUP BY sp.speaker_id, sp.name, sp.description
ORDER BY most_recent_episode DESC;
Speaker and Series Combination
Which speakers taught which series?
-- Speakers by series
SELECT
s.title as series_title,
sp.name as speaker_name,
COUNT(e.episode_id) as episodes_in_series,
MIN(e.published_live_at) as first_episode,
MAX(e.published_live_at) as last_episode
FROM planning_center.publishing_series s
JOIN planning_center.publishing_episodes e
ON s.series_id = e.series_id
JOIN planning_center.publishing_speakerships ship
ON e.episode_id = ship.episode_id
JOIN planning_center.publishing_speakers sp
ON ship.speaker_id = sp.speaker_id
GROUP BY s.series_id, s.title, sp.speaker_id, sp.name
ORDER BY s.started_at DESC, episodes_in_series DESC;
Content Performance
Episode View Counts
Track engagement metrics for episodes.
-- Episode performance metrics
SELECT
e.title as episode_title,
e.published_live_at,
est.view_count,
est.download_count,
est.unique_viewers,
est.average_watch_time / 60 as avg_watch_minutes,
s.title as series_title
FROM planning_center.publishing_episodes e
LEFT JOIN planning_center.publishing_episode_statistics est
ON e.episode_id = est.episode_id
LEFT JOIN planning_center.publishing_series s
ON e.series_id = s.series_id
WHERE e.published_live_at >= CURRENT_DATE - INTERVAL '3 months'
ORDER BY est.view_count DESC NULLS LAST
LIMIT 25;
Top Performing Content
Your most viewed episodes.
-- Top 10 most viewed episodes of all time
SELECT
e.title,
e.published_live_at,
est.view_count,
est.download_count,
est.unique_viewers,
s.title as series_title,
EXTRACT(DAY FROM (CURRENT_DATE - e.published_live_at)) as days_since_published
FROM planning_center.publishing_episodes e
JOIN planning_center.publishing_episode_statistics est
ON e.episode_id = est.episode_id
LEFT JOIN planning_center.publishing_series s
ON e.series_id = s.series_id
WHERE est.view_count IS NOT NULL
ORDER BY est.view_count DESC
LIMIT 10;
Weekly Engagement Trends
How does engagement change over time?
-- Weekly engagement summary
SELECT
DATE_TRUNC('week', e.published_live_at) as week,
COUNT(DISTINCT e.episode_id) as episodes_published,
SUM(est.view_count) as total_views,
SUM(est.download_count) as total_downloads,
AVG(est.view_count) as avg_views_per_episode,
SUM(est.unique_viewers) as total_unique_viewers
FROM planning_center.publishing_episodes e
LEFT JOIN planning_center.publishing_episode_statistics est
ON e.episode_id = est.episode_id
WHERE e.published_live_at >= CURRENT_DATE - INTERVAL '12 weeks'
GROUP BY week
ORDER BY week DESC;
Channel Distribution
Channel Overview
See all your distribution channels.
-- List all channels with settings
SELECT
channel_id,
name,
kind,
description,
streaming_service,
church_center_enabled,
default_publish_time,
time_zone
FROM planning_center.publishing_channels
ORDER BY name;
Episodes by Channel
Content distribution across channels.
-- Episode count by channel
SELECT
c.name as channel_name,
c.kind as channel_type,
COUNT(DISTINCT e.episode_id) as episode_count,
MIN(e.published_live_at) as first_episode,
MAX(e.published_live_at) as latest_episode
FROM planning_center.publishing_channels c
LEFT JOIN planning_center.publishing_episodes e
ON c.channel_id = e.channel_id
GROUP BY c.channel_id, c.name, c.kind
ORDER BY episode_count DESC;
Resources and Materials
Episode Resources
Find downloadable materials for episodes.
-- Resources for recent episodes
SELECT
e.title as episode_title,
e.published_live_at,
er.name as resource_name,
er.kind as resource_type,
er.url,
er.file_size / 1048576 as size_mb
FROM planning_center.publishing_episodes e
JOIN planning_center.publishing_episode_resources er
ON e.episode_id = er.episode_id
WHERE e.published_live_at >= CURRENT_DATE - INTERVAL '1 month'
ORDER BY e.published_live_at DESC, er.name;
Resource Types Summary
What types of resources are you providing?
-- Count resources by type
SELECT
kind as resource_type,
COUNT(*) as resource_count,
COUNT(DISTINCT episode_id) as episodes_with_type,
SUM(file_size) / 1073741824 as total_size_gb
FROM planning_center.publishing_episode_resources
GROUP BY kind
ORDER BY resource_count DESC;
Publishing Schedule
Upcoming Publishing Times
When will content be published next?
-- Next scheduled publishing times by channel
SELECT
c.name as channel_name,
cnt.next_publish_at,
EXTRACT(DAY FROM (cnt.next_publish_at - CURRENT_TIMESTAMP)) as days_until_publish,
c.default_publish_time
FROM planning_center.publishing_channel_next_times cnt
JOIN planning_center.publishing_channels c
ON cnt.channel_id = c.channel_id
WHERE cnt.next_publish_at > CURRENT_TIMESTAMP
ORDER BY cnt.next_publish_at;
Episode Publishing Schedule
When episodes are scheduled to go live.
-- Episode publishing schedule
SELECT
e.title as episode_title,
et.published_at as scheduled_time,
c.name as channel_name,
et.status as publish_status,
e.published_live_at as actual_publish_time
FROM planning_center.publishing_episode_times et
JOIN planning_center.publishing_episodes e
ON et.episode_id = e.episode_id
JOIN planning_center.publishing_channels c
ON et.channel_id = c.channel_id
WHERE et.published_at >= CURRENT_DATE
ORDER BY et.published_at;
Quick Metrics
Publishing Summary Dashboard
Key metrics at a glance.
-- Publishing dashboard metrics
SELECT
(SELECT COUNT(*) FROM planning_center.publishing_episodes
WHERE published_live_at IS NOT NULL) as total_episodes,
(SELECT COUNT(*) FROM planning_center.publishing_series
WHERE published = true) as total_series,
(SELECT COUNT(*) FROM planning_center.publishing_speakers) as total_speakers,
(SELECT COUNT(*) FROM planning_center.publishing_channels) as total_channels,
(SELECT SUM(view_count) FROM planning_center.publishing_episode_statistics) as total_views,
(SELECT SUM(download_count) FROM planning_center.publishing_episode_statistics) as total_downloads,
(SELECT COUNT(*) FROM planning_center.publishing_episodes
WHERE published_live_at >= CURRENT_DATE - INTERVAL '30 days') as episodes_last_30_days,
(SELECT COUNT(*) FROM planning_center.publishing_episode_resources) as total_resources;
Monthly Publishing Cadence
How consistent is your publishing schedule?
-- Monthly publishing statistics
SELECT
DATE_TRUNC('month', published_live_at) as month,
COUNT(*) as episodes_published,
COUNT(DISTINCT series_id) as active_series,
COUNT(DISTINCT DATE_TRUNC('week', published_live_at)) as weeks_with_content,
ARRAY_AGG(DISTINCT EXTRACT(DOW FROM published_live_at)) as publishing_days
FROM planning_center.publishing_episodes
WHERE published_live_at >= CURRENT_DATE - INTERVAL '12 months'
AND published_live_at IS NOT NULL
GROUP BY month
ORDER BY month DESC;
Tips for Using These Queries
- Customize Date Ranges - Change INTERVAL values to match your needs
- Filter by Channel - Add
AND channel_id = 'YOUR_CHANNEL_ID' to focus on specific channels
- Add Speaker Filters - Join with speakerships to analyze specific speakers
- Export Results - Use your SQL client’s export feature for reports
- Schedule Reports - Many of these work great as automated weekly/monthly reports
Next Steps
Ready for more complex analysis? Check out:
Start simple, build confidence, then explore more complex queries as you grow!