Now, what does AI think? Earlier, I mentioned we'd run this through an AI tool and see how it compares. I'm using Microsoft Copilot for this. If you're working with SQL Server, there's a reasonable chance your organization already has access to Copilot at an enterprise level. One important thing before we go any further, you need to confirm which AI tools you're allowed to use at work, especially around data privacy. You're essentially putting your query and your execution plan into an AI prompt. That could include sensitive business logic. So, make sure you know what's allowed before you do this with production code. For this video, the query and execution plan are from a sample database, so it's fine. I'll paste in the prompt and add the original query. For the execution plan, I couldn't attach the file directly because of a format restriction. For the execution plan, I've added in the screenshots from earlier, one from the left side and one from the right. I'll attach them all and let Copilot it together. Let's see what it found. There's a summary at the top. It says the query is suffering from correlated scalar subqueries executed repeatedly per row. This is something that we covered in this video. There are massive scans, cardinality misestimation, expensive joins, indexes, and aggregations. Let's scroll down to learn more. The first suggestion was correlated subqueries. It says each of these forces SQL Server to rescan SalesOrderDetail once per row of SalesOrderHeader. This is exactly what we found in our analysis and was the biggest cause of the slow performance. Copilot explains why it is bad and what the fix is, which is what we did. The next suggestion is a correlated subquery for customer name, which is the same problem just with lesser impact. The next section is about execution plan red flags, including the huge scan on SalesOrderDetail. The suggested fix is to create a covering index, which is exactly what we did. Next, there is a point about the merge and hash joins and why they are bad. It says the fix is covered by indexes and query rewrite, which is what we did earlier. The next point is something we didn't catch ourselves, cardinality misestimates. This means the database thinks there are a different number of rows in a table than there actually are. In this case, it could be a side effect of the script [clears throat] I ran to add extra rows for testing purposes, which might not have triggered a statistics update. Statistics are what the database uses to estimate row counts when building a query plan. If they're out of date, the estimates are wrong and the database might choose a less efficient approach. Updating statistics is something we could have done and didn't. The fix for this is to update statistics on these two tables. We'll come back to this. We can keep scrolling and see this point about a non-sargable predicate, which is the year function we addressed earlier in the video. Copilot has also provided a rewritten version of the query. It uses a CTE to pre-aggregate the product totals. The approach is similar to ours, just structured a little differently. I won't run this as it should show the same results and performance. We've got a list of recommendations here, then a summary table, which is a nice output. The AI analysis was useful. It confirmed most of what we found, and it surfaced one thing we missed. Let's update those table statistics that Copilot suggested. We can scroll up and see the commands here. I can copy these and paste them into SSMS. Then I can run them. After a minute or so, the commands are completed. Let's run the main query again. We run this and see the results here. It shows the right number of rows as a total, and the run time is 4 seconds. So, it seems this suggestion has helped the run time. Let's look at what we did across these steps and what it tells us. We started with a 30-second query that had six correlated subqueries in the select clause and a non-sargable date filter in the where clause. The first thing we fixed was the year function on order date, not because it gave us an immediate speed improvement, but because it was structurally wrong and would prevent an index from helping later. Then we removed the correlated subqueries. The customer name subquery became a join. The five sales order detail subqueries were consolidated into a single group subquery, joined in once with a left join. After those changes, the query ran in 15 seconds. No index is added, but we had a better structure. Then we added two indexes, one on sales order header covering the filter and join columns, and a covering index on sales order detail covering the aggregation. That brought it down to 6 seconds. Finally, we updated the statistics as suggested by Copilot, which brought the query down to 4 seconds. A few things are worth taking away from this. The first is that query structure matters before indexes do. Indexes run on a query that runs the same subquery 90,000 times are not going to solve the problem. Fix the structure first, then think about indexes. The second is that correlated subqueries in the select clause are worth checking for in any slow query. They look harmless, but they run once per row. You might not notice them on small tables. At 90,000 rows, they cost you a lot of time. And the third is the row count check. When we introduced the join on sales order detail, we accidentally changed the row count by using an inner join when we needed a left join. Catching that required checking the results, not just the run time. You should always verify both. If you want the AI prompt I used in this video, it's available as a free download at the link in the description. You can use it with your own slow queries. Just add your SQL and attach your execution plan, and it'll give you a structured analysis to work from. If you found this useful, you'll want to watch this video next, where I go through the same process on another query and see how it compares. Thanks for watching.