Use pg_cron to schedule SQL jobs that refresh materialized views or run ad-hoc queries, producing hands-free, recurring reports.
pg_cron lets you run SQL statements on a schedule directly inside PostgreSQL, removing the need for external cron jobs or orchestration tools.
Connect as superuser and run:
CREATE EXTENSION IF NOT EXISTS pg_cron;
On managed services, enable the extension in the control panel first.
Encapsulate expensive aggregations in a materialized view so that each refresh replaces the whole report quickly.
CREATE MATERIALIZED VIEW monthly_sales_summary AS
SELECT DATE_TRUNC('month', o.order_date) AS month,
SUM(o.total_amount) AS revenue,
COUNT(o.id) AS order_count
FROM Orders o
GROUP BY 1;
Use cron.schedule()
to run the refresh after business hours:
SELECT cron.schedule(
job_name => 'refresh_monthly_sales',
schedule => '0 1 1 * *', -- 1 AM on the 1st of every month
command => $$REFRESH MATERIALIZED VIEW CONCURRENTLY monthly_sales_summary;$$
);
Wrap the query in a server-side function that uses pgenv
or an SMTP extension, then schedule the function with pg_cron.This keeps credentials inside the database.
Query cron.job
to inspect jobs and use cron.unschedule(jobid)
to remove them. Update schedules with cron.schedule()
on the same job_name.
• Always mark long-running refreshes as CONCURRENTLY
to avoid locks.
• Log job output by setting log_min_messages = info
.
• Store results in materialized views or tables, not temporary files, for easier reuse.
.
Managed services often disable superuser access, so pg_cron may not be available. Check provider docs; some offer a built-in scheduler alternative.
Yes. Jobs are stored in cron.job
and reload automatically after the server restarts.
Enable log_min_messages = info
and review the PostgreSQL log or query cron.job_run_details
for status and error messages.