Back to Resources
Blog

Was That Query Always This Slow? The Query-History Gap in MySQL, SQL Server, and DB2

Andre Beaumont·August 24, 2026

Title card on graph paper reading Was That Query Always This Slow, with the subtitle Your database knows what's expensive right now, it has no idea what got slower, and a line beneath reading Bridging the query-trend gap in MySQL, Db2, and SQL Server. Below the text, a blue line runs flat from an orange origin dot, then breaks into a series of peaks and troughs across four marked time points labelled T1 through T4 before flattening out into an arrowhead

A query is slow this morning. Someone asks the obvious next question: has it always been like that, or did something change? You pull up the live stats, and there it is, expensive and near the top of the list. But that view only tells you it's slow now. Whether it was this slow last Tuesday, or quietly doubled after Thursday's deploy, isn't in front of you. So you go grep an old slow-log file, line up deploy timestamps by hand, or just guess.

Here's the gap, up front. Your database keeps a running tally of how each query performs, but not its history. The number it shows you is a cumulative total since the last restart, or a figure that lives in a cache and gets evicted as the cache fills. There's no time axis, so "is this query regressing, and when did it start?" has no native answer. For MySQL and Db2, the Integration Plumbers plug-ins close that gap the unglamorous way: they snapshot the per-query stats into the Oracle Enterprise Manager repository on a schedule, so every statement gets a trend line you can read over any window. SQL Server is the interesting exception, and the exception is the best evidence the whole idea matters.

This post has three parts: why "slow now" isn't "getting slower," the per-query record each database keeps and what it forgets, and why the repository is where the trend belongs.



1. "Slow now" isn't "getting slower"

The native per-query views are good at one question: of everything that has run, what's expensive? What they don't carry is a time dimension. They hand you a single accumulated number per query, not a series of points you can draw a line through. That's fine for "what should I look at today" and useless for "what changed."

And "what changed" is the question that actually shows up in incidents. A query that's slow and always has been is a tuning task you can schedule. A query that was fine until 2 p.m. Thursday is an incident with a cause you can still find. Telling those two apart needs history, and history is what the live view doesn't keep. So the investigation falls back to slow-log files and deploy-log archaeology, days after the evidence you wanted has already rolled off.

Blueprint-style panel headed To schedule a task, you need a speedometer. To investigate an incident, you need a seismograph. On the left, a boxed illustration labelled The Native View shows a mechanical gauge with a single needle over exposed gearwork, captioned a single accumulated number, no time axis. On the right, a boxed illustration labelled The Missing View shows a seismograph drum unrolling a paper trace marked Day 1 through Day 7 with a jagged blue line recorded across it, captioned a series of points you can draw a line through. Text beneath reads that the native per-query views hand you a cumulative total rather than a temporal baseline, and that without a time dimension, answering when did this start forces an immediate fallback to slow-log archaeology and guesswork

There's a subtler trap underneath this, and it's the reason you can't fix the problem just by charting what's already there. These counters are cumulative. Bind one straight to a graph and you get a line that only ever climbs, which tells you nothing about load in any particular hour. To get a useful trend you have to store consecutive readings and subtract them, and then deal with every way that subtraction can lie to you. More on that in part 3, because it turns out to be most of the work.

2. The record each database keeps, and what it forgets

Each of these databases already computes rich per-query numbers. The catch is what happens to them over time.

MySQL. performance_schema aggregates every normalized statement into a digest summary with the good stuff: execution counts, total and average latency, lock time, rows examined versus rows sent, and how often a statement ran without using an index. It's genuinely useful, and it's a running total with no history. Reset it, or restart the server, and it starts over from zero. The table is also bounded in size. Once you exceed performance_schema_digests_size, everything past the limit collapses into a single anonymous overflow row with a null digest, so the long tail stops being individually attributable exactly when your workload is most varied.

Db2. On Db2 for Linux, UNIX and Windows, MON_GET_PKG_CACHE_STMT gives you the same shape of per-statement metrics from the package cache: executions, activity time, CPU time, lock wait time, rows read. Same catch, sharper edge. Those counters are cumulative since the statement entered the cache rather than since the instance started, and the package cache evicts entries as it fills. So the statements you most want to look back on are often the ones already aged out, and the ones still there are counting from a starting line you didn't choose and can't see.

SQL Server. Here's the exception, and it's the one that proves the point. Microsoft built query history into the engine. Query Store retains query text, plans, and runtime statistics over a configurable window, and it's on by default for new databases as of SQL Server 2022. When a database vendor ships this as an engine feature and turns it on for everybody, that's a strong statement about what the missing history costs.

A three-column table headed The Database Amnesia Matrix, comparing MySQL, Db2, and SQL Server across three rows. Source: MySQL performance_schema, Db2 MON_GET_PKG_CACHE_STMT, SQL Server Query Store. The Catch: MySQL resets to zero on server restart, Db2 is cumulative only since entering the package cache, and SQL Server is marked simply The Exception. The Memory Loss: MySQL is bounded by a limit, and when full the long tail collapses into a single anonymous overflow row just when a varied workload needs tracking most; Db2's cache evicts entries as it fills, so the statements you most want to investigate are often the ones just aged out and the survivors are counting from an invisible starting line; and on SQL Server, Microsoft built history natively into the engine, default since 2022, proving the industry-wide cost of missing historical data

It also shaped the order we built in. MySQL and Db2 had no equivalent, so those are the two that got the repository-side trend first. On SQL Server today the plug-in's Queries page shows live plan-cache statistics, top statements by CPU and by execution count out of sys.dm_exec_query_stats, plus a rollup per execution plan. Surfacing Query Store itself, and the plan-regression analysis that comes with it, is a tracked roadmap item rather than something in the beta. Worth saying plainly: if you're on SQL Server 2022 or later, that history is accumulating in your database right now whether or not anything is reading it yet. If you're on something older, turning Query Store on is worth doing today no matter what ends up monitoring it.

3. Why the Oracle Enterprise Manager repository is where the trend belongs

The fix isn't clever. It's having somewhere to keep the numbers over time. The MySQL and Db2 plug-ins take periodic delta snapshots of the per-query stats into the Enterprise Manager repository, so each statement gets a trend you can read over any window you choose. Each one is tracked under a short stable key rather than under its text, which never gets persisted. MySQL snapshots every five minutes and keeps the top 25 statements per collection, keyed by the performance_schema digest hash. Db2 snapshots every fifteen minutes and keeps the top 200, keyed by a hash the agent computes from the statement text and then discards the text. What you get out of it:

  • Top-N over a real window. Rank by latency, execution count, lock time, or rows read over windows running from real time out to the last month, instead of "everything since the server last restarted."
  • Regression detection. This statement's average latency tripled on Thursday afternoon, with the line that shows it and the timestamp to line up against your deploy log.
  • In one place. Next to the host, replication, and backup metrics, in the console you already run the estate from.

The unglamorous part is making the subtraction honest, and it's worth a paragraph because it's where this kind of feature usually goes wrong. Db2's collector is a good example. If a cached statement gets evicted and comes back, its counters restart at zero, and a naive difference would show a drop that never happened. If a busy database holds more statements than the collector samples, one can fall below the cut for a single cycle and reappear, and reading "absent last time" as "brand new" would report hours of accumulated work as one fifteen-minute window: a spike for a statement that never got busier. So the collector compares each entry's insert timestamp against a clock reading taken with the previous snapshot, both from the database rather than the agent so there's no skew to argue about, and it flags a re-cached statement as a partial window instead of quietly under-reporting it. On the very first collection it emits nothing at all and just records the baseline, because one empty window beats one wrong window.

Blueprint-style chart headed The Unglamorous Work of Honest Math, plotting two lines across a Db2 query lifecycle marked Entry, Eviction, and Re-entry. A dashed orange line labelled Naive Math holds flat through eviction, then slides to zero at re-entry in a fall annotated False performance drop, and stays at zero thereafter. A solid blue line labelled Honest Math tracks the real values, breaks cleanly at eviction, and resumes at re-entry against an accurate temporal baseline. Two panels beneath: one headed Subtraction can lie, explaining that a cached Db2 statement that is evicted and returns restarts its counters at zero, so a naive difference shows a performance drop that never happened; and one explaining that the collector compares each entry's insert timestamp against a clock reading taken from the database, that re-cached statements are flagged as partial windows and empty first collections emit a baseline, and that one empty window beats one wrong window

What you get is numeric trend history keyed to each statement: how a given query's numbers move over time. What you don't get, in this release, is a searchable archive of full statement text and execution plans across history. Keeping every query's text and plan over time is a heavier and different storage problem, and it's the next step on the roadmap, built on the same purpose-built store the PostgreSQL plug-in is pioneering. There's also a real cap: the trend view can only aggregate the statements the collector kept per cycle, so the deep tail isn't in there. We'd rather say so than let you find out from a chart. For the question this post is about, "is this query regressing, and when did it start," the answer is a numeric trend, and that's what's arriving now.



Wrapping up

Your database is good at telling you what's slow this second and oddly bad at telling you what changed. The metrics to answer "did this regress" already exist. They just need somewhere to live over time, and something careful enough to subtract them correctly. That, in two sentences, is the feature.

For whoever's on call, it turns a familiar shrug into a straight answer. "Has this query always been like this?" stops being a slow-log expedition and becomes a line on a graph with a date on it. And for the SQL Server people reading this: you may already have the history and not know it. Go check whether Query Store is on.

We covered why "slow now" isn't "getting slower," the per-query record MySQL, SQL Server, and Db2 each keep and what each forgets, and why the trend belongs in the Oracle Enterprise Manager repository.

This is the seventh post in a series on the blind spots your estate monitoring leaves across MySQL, SQL Server, and DB2, and it closes the set. The earlier six were The Backup You Can't See, on whether last night's backup actually succeeded; The DR-Readiness Gap, on whether a "synchronized" cluster could really fail over without losing data; The Clock Ran Out on SQL Server 2016, on monitoring through an end-of-support migration; After MEM, on continuity for MySQL monitoring; No Cliff, No Drama, on monitoring DB2 11.5 and 12.1 and the new ground ahead; and Least-Privilege by Design, on the read-only, TLS-first posture all three plug-ins share. Seven posts on the gaps we kept finding between what a database knows about itself and what its monitoring can actually tell you.

Historical query analytics is one of the capabilities arriving with our Enterprise Manager plug-ins. Open Beta opens September 2026. Want it trending your queries first? Learn more and sign up:

If you would rather talk through what a trend line would show on your own workload before signing up, talk to us.


Why can't I just chart the per-query numbers my database already has?+

Because they are cumulative counters, not a series. Every one of these views hands you a single accumulated total per statement, from a starting point you did not choose: since the last server restart on MySQL, since the statement entered the package cache on Db2. Bound straight to a graph, a cumulative counter draws a line that only ever climbs, which tells you nothing about load in any particular hour. To get a trend you have to store consecutive readings and subtract them, and then handle every way that subtraction can go wrong, which is most of the actual work.

How often does the plug-in snapshot query statistics, and how many statements does it keep?+

On MySQL, every five minutes, keeping the top 25 statements per collection keyed by the performance_schema digest hash. On Db2, every fifteen minutes, keeping the top 200 per collection, keyed by a hash computed from the statement text. Those caps exist because a busy database can hold thousands of cached statements, and persisting every one on every cycle costs more storage than the answer is worth. The honest consequence is that the deep tail of your workload is not in the trend view, only the statements that made the cut each cycle.

Is the statement text stored in the Oracle Enterprise Manager repository?+

Not for the trend history. Each statement is tracked under a short stable key derived from its text, and the text itself is not persisted with the trend data. That is a deliberate design choice for this release: it keeps the storage cost of a per-statement time series proportional to the numbers rather than to your query text, and it keeps query literals out of the monitoring repository. The trade-off is stated plainly in the post: this is numeric trend history, not a searchable archive of full text and plans.

What happens when a statement gets evicted from the cache and comes back?+

It is detected and flagged rather than charted wrong. When a statement is re-cached its counters restart at zero, so a naive current-minus-previous subtraction would show a drop that never happened. The collector reads the entry's insert timestamp and compares it against a clock reading taken with the previous snapshot, both read from the database so there is no agent-versus-database clock skew to argue about. A re-cached statement is reported as a partial window instead of being quietly under-reported. There is a related trap the same check catches: on a database with more cached statements than the collector samples, one can fall below the cut for a single cycle and reappear, and reading "absent last time" as "brand new" would report hours of accumulated work as a single fifteen-minute window.

Why does the first collection show nothing?+

Because there is no previous snapshot to subtract from, so every statement would look brand new and its entire accumulated history would be reported as one window. The first collection after a fresh start, an agent restart, or an expired cache emits zero rows and records only the baseline. Normal reporting begins on the next cycle. One empty window beats one wrong window.

I'm on SQL Server. Do I need this at all?+

You already have most of it, and that is the point. Query Store retains query text, plans, and runtime statistics over a configurable window, and it has been on by default for new databases since SQL Server 2022. If you are on 2022 or later, that history is accumulating right now whether or not anything is reading it. If you are on something older, turning Query Store on is worth doing today regardless of what ends up monitoring it. What the plug-in shows on the Queries page today is live plan-cache statistics: top statements by CPU and by execution count from sys.dm_exec_query_stats, plus a rollup per execution plan. Surfacing Query Store itself, with the plan-regression analysis that comes with it, is a tracked roadmap item rather than part of the beta.

What is the difference between this and the top-SQL view I already get?+

A top-SQL view ranks what is expensive right now, out of whatever the database currently has cached. This ranks what was expensive during a window you choose, out of a stored series, which is a different question with a different answer. It is what lets you say a statement's average latency tripled on Thursday afternoon and put a timestamp on it, rather than only that it is near the top of the list this morning. Both are useful. The trend is the one that turns "has this always been like that?" into something you can answer without a slow-log expedition.

Will there ever be full query text and execution plan history?+

Yes, it is the next step on the roadmap. Keeping every statement's text and plan across history is a heavier and different storage problem from keeping its numbers, and it is being built on the same purpose-built store the PostgreSQL plug-in is pioneering. What is arriving now is the numeric trend, because that is what answers the question this post is about: is this query regressing, and when did it start.

Related Resources

Least-Privilege by Design: One Read-Only, TLS-First Way to Monitor DB2, SQL Server, and MySQL
Blog

Least-Privilege by Design: One Read-Only, TLS-First Way to Monitor DB2, SQL Server, and MySQL

Every database monitoring tool eventually meets the security team, and the answer is usually a broad-privilege account plus an agent installed on the database host. That's a design choice, not a law of nature. Here's the posture shared by all three Integration Plumbers plug-ins for Oracle Enterprise Manager: one read-only grant per engine, an encrypted connection that won't quietly downgrade, your choice of where collection runs, and why that same design reaches a managed cloud database without a rebuild.

Aug 13, 2026

No Cliff, No Drama: Db2 11.5 to April 2027, 12.1 Open-Ended, and the New Ground Ahead
Blog

No Cliff, No Drama: Db2 11.5 to April 2027, 12.1 Open-Ended, and the New Ground Ahead

While MySQL and SQL Server admins spent this summer watching end-of-support dates pass, DB2's is still ahead: IBM supports Db2 LUW 11.5 until April 30, 2027, and hasn't published an end-of-support date for 12.1 at all. Here's why that runway makes now the right time to fix DB2 monitoring rather than scramble at it, the compliance rule that flags your 11.5 estate the day the date passes, what the DB2 Plug-in for Oracle Enterprise Manager closes that even IBM's free Data Management Console leaves open, and the ground on the roadmap in Db2 for z/OS and the 12.1 AI Query Optimizer.

Jul 29, 2026

After MEM: The Next Chapter for MySQL Monitoring in Oracle Enterprise Manager
Blog

After MEM: The Next Chapter for MySQL Monitoring in Oracle Enterprise Manager

MySQL Enterprise Monitor reached end of life in January 2025, and Oracle guided customers to its Enterprise Manager plug-in as the path forward. Oracle has since published a deprecation notice for that plug-in through My Oracle Support. The MySQL Plug-in for Oracle Enterprise Manager from Integration Plumbers provides continuity: the same console, the MEM features the Oracle plug-in didn't include, and a collection layer built for MySQL 8.4.

Jul 16, 2026

Not Sure Where to Start?

Take our free OTEL Maturity Assessment to identify gaps and get a personalized action plan.

Take the Free Assessment