All Posts
C#WPFMySQL

How I Reduced a Report Generation Job from 45 Minutes to 10 Seconds

This is a placeholder description for your first blog post. Replace it with your actual content once you're ready to publish.

· ~4 min read

I joined a C# / WPF project that was already released for a client to help debug some problems, and one of the first issues I had to deal with was report generation performance.

The application had to generate XLS files and a PDF preview for a document. At first, it worked only for a single day of data, and even then it could take several minutes to finish.

The goal was not only to fix the performance issue, but also to improve the feature so users could select a period of time instead of being limited to one day. In other words, the system needed to support generating reports for several months at once. At the same time, the PDF generation could be removed if it was too expensive, since it was not strictly required.

Understanding the problem

When I studied the code, I quickly found that the performance issue was not coming from one single place, but from several small problems that combined into something much worse.

1. Nested loops causing excessive queries

The first big issue was a nested loop structure that looked roughly like this:

  • loop over a list of items
  • for each item, loop over several rows
  • inside that second loop, perform multiple database queries

That meant the number of queries exploded very quickly.

For one day of data, the application could easily reach around 4,500 queries just to gather the information needed for a single document. And once the feature had to support longer periods, the problem became completely unsustainable.

2. Repositories fetching data one piece at a time

The second issue was in the data access layer.

Several repository methods were fetching data in a very fragmented way. Instead of retrieving everything needed in one go, they would:

  • get an ID
  • query the name
  • query a few additional fields
  • query another related piece of data

This was repeated across multiple repositories.

The problem was not that each query was expensive on its own. The problem was that the application was asking for the same data structure again and again, but in many small parts instead of in a single efficient fetch.

3. PDF generation overhead

The PDF preview was also contributing to the slowness.

I did not fully investigate every internal detail of that part, but removing it made the process noticeably faster. It was not the main cause of the performance issue, but it clearly added overhead.

What I changed

My main approach was to stop querying the database repeatedly during the generation loop.

Instead, I preloaded the required data in advance from the repositories, stored it in memory, and then used that collected data to build the output.

This changed the workflow from:

  • query data repeatedly during processing to:

  • fetch data once

  • organize it in memory

  • generate the report using the preloaded data

That made the logic much more efficient and much easier to reason about.

Once all the data was collected, I could iterate through it and match the correct values together to generate the XLS file properly.

I also adjusted the generation flow so it could handle larger date ranges instead of just a single day.

Result

The improvement was dramatic.

What had originally taken around 45 minutes for one month of data was reduced to around 10 seconds, even for a few months of data.

That was a huge difference, and it completely changed how usable the feature was.

What I learned

This project was a good reminder that performance problems are often caused by small design decisions that multiply together:

  • nested loops
  • repeated queries
  • fragmented repository access
  • unnecessary processing steps

It also showed me the value of looking at the full flow instead of focusing on one line of code at a time.

Sometimes the real fix is not a small optimization, but changing the way data is loaded and processed altogether.