Someone opens the slow query log looking for the cause of a performance problem. At the top is a report taking four seconds, run once an hour by a scheduled job. It is the slowest thing in the log and it is consuming four seconds of database time per hour.
Meanwhile a query taking 40 milliseconds runs ten thousand times a minute, consuming four hundred seconds of database time per minute. It never appears in the log, because it is fast.
What changed in 2026
- Aggregated statistics displaced threshold logs. Extensions tracking cumulative statistics per normalised query became the primary tool.
- Managed platforms exposed them by default. Query insights became a standard feature rather than something to configure.
- Plan capture improved. Some tooling began capturing plans alongside statistics, removing a manual step.
- Parameter logging stayed contentious. The tension between reproducibility and logging sensitive values persisted.
Total time is the metric
Rank queries by cumulative execution time, not by worst individual duration.
| Query |
Duration |
Calls/hour |
Total/hour |
| Hourly report |
4 s |
1 |
4 s |
| Dashboard widget |
200 ms |
500 |
100 s |
| Session lookup |
40 ms |
600,000 |
24,000 s |
The session lookup is the problem, and it never appears in a slow query log at any sensible threshold. It is not slow. It is enormous in aggregate.
That is why aggregated query statistics — which track calls, total time, mean time, and rows per normalised query — are the primary tool. They surface exactly this case, which threshold logging is structurally incapable of finding.
Normalisation matters: the extension groups queries by structure with parameters stripped, so ten thousand executions with different values appear as one entry with a call count.
What a slow query log is still for
Aggregated statistics tell you which query shape costs most. They do not tell you what happened in a specific bad execution.
A threshold log catches individual events: the query that took thirty seconds because it hit a lock, or because a parameter matched far more rows than usual. Aggregated statistics average that away.
So both have a role. Statistics for finding what to optimise; the log for investigating specific incidents.
Set the threshold high enough that logging does not become its own load. A very low threshold on a busy database produces enormous log volume and can measurably slow the system — which is a real, if embarrassing, way to make performance worse while investigating performance.
Making entries actionable
A logged slow query you cannot reproduce is of limited use.
Log the parameters where you can. A normalised query without values cannot be re-run, and the values are frequently the reason it was slow — a parameter matching a million rows rather than ten. This conflicts with keeping sensitive values out of logs, and the usual compromise is logging parameters for non-sensitive queries and redacting elsewhere.
Capture the plan for slow executions where your platform supports it. The plan at the time of the slow execution is far more informative than a plan generated later against different data.
Log the duration and rows returned. A query returning a million rows is slow for an understandable reason.
Include enough context to attribute it — the application, the endpoint, the user if appropriate. A slow query with no owner is hard to prioritise.
Common mistakes
- Optimising the slowest query. Frequently not the most expensive.
- Relying only on a threshold log. Misses high-frequency queries entirely.
- Threshold set very low. Logging becomes the bottleneck.
- No parameters logged. Cannot reproduce.
- Not resetting statistics after a change. Cumulative counters hide the improvement.
- Ignoring rows-returned. Explains many slow queries immediately.
- No attribution. Cannot tell which team owns the problem.
FAQ
How do I measure the effect of a fix?
Reset the statistics after deploying, then compare over a comparable period. Cumulative counters from before the change dilute the result.
Should I log every query?
On a busy system, no — the volume is unmanageable and the logging itself costs. Aggregated statistics give the overview; the log catches outliers.
What about queries that are slow only sometimes?
Aggregated statistics show a high maximum against a low mean, which is the signature. The log then catches individual instances for investigation, and the cause is frequently lock waiting rather than the query itself.
Does this replace application tracing?
No — application tracing shows which request triggered which queries, which the database cannot know. They complement each other.
Where to go next
For the aggregated statistics tooling, read pg_stat_statements. For diagnosing a specific slow query, query planners, and for bounding the damage, statement timeouts.