SQL
Query Store Forced Plan Failures
Query Store is a fantastic feature of both SQL Server and Azure SQL DB. It allows you to monitor how queries execute against the database which is invaluable for troubleshooting performance issues. More than that though, it gives you the option to force an erratic query to use a particular execution plan, this helps avoid queries from running with inefficient plans and provides predictability and stability on the server.
Here we can see an example of a query that hasn’t been forced and the engine is free to execute it however it sees fit, as you can see, the variation of execution time is erratic and unpredictable:
Here we can see an example of a query that has been forced to use a nicely optimised plan:
Here we can see an example of a query that has been optimised but the forcing failed in one instance, resulting in an average execution time of over 2 minutes, whereas before it was only 114ms, a huge performance issue when you factor in that this query runs multiple times per minute.
Plan forcing isn’t guaranteed and can fail for various reasons, the documentation from Microsoft reads:
“When a plan is forced for a particular query, every time SQL Server encounters the query, it tries to force the plan in the Query Optimizer. If plan forcing fails, an Extended Event is fired and the Query Optimizer is instructed to optimize in the normal way.”
If a plan does fail, the query will still execute, the optimiser will just default back to its normal behaviour. The good news is we can monitor the failures through extended events, and the Query Store GUI (although this only shows the last one):
Below are the reasons and explanations for potential failures:

These potential failures reinforce the point that using Query Store isn’t a matter of forcing a plan and forgetting about it, even if you’re using Automatic Plan Correction (APC), you should always actively monitor and manage it.
The SQL Query Alias Conundrum – Order of Execution
So, you have just written a query, hit execute and you have encountered an error: Invalid column name ‘[column name]‘.
The column you’ve used in your WHERE clause cannot be identified by its alias. You’ve defined it at the top of the query and used it fine previously as your ORDER BY condition, so why can’t the engine recognise it?



If you’ve ever written a SQL query, this should look familiar:
- SELECT
- DISTINCT
- FROM
- JOIN
- ON
- WHERE
- GROUP BY
- HAVING
- ORDER BY
It makes sense that we read the query from top to bottom / left to right (especially for those in the West), but contrary to how it may look, this isn’t how the query is executed. The order of execution is:
- FROM / JOIN
- WHERE / ON
- GROUP BY
- HAVING
- SELECT
- DISTINCT
- ORDER BY
When you stop and think about what is happening, logically, this order makes sense.
FROM / JOIN – First and foremost, we need to determine the location of the data set that we are querying.
WHERE / ON – We need to narrow that data set down with predicates to keep only the records we care about.
GROUP BY – If you need to aggregate the results, this is done next to give us an even more concise result set.
HAVING – WHERE 2.0! This is a predicate over the aggregated result set.
SELECT – Okay, now we have our final result set, containing ONLY our required rows. Now, finally, we can select which columns we want from that dataset.
DISTINCT – Now we know which columns we want from the result set, we can strip out any duplicates.
ORDER BY – Finally, we have whittled the result set down to only the rows and columns we need, so we can order it however we want.
What is the value in knowing this?
It’s important to understand the difference between the logical and lexical order of a query. Knowing this will help you troubleshoot any syntax errors you may encounter.
Going back to our original issue of using an alias in a WHERE clause, you can now see why you get an error about an invalid column.
Look back at the order of execution, aliases are assigned to columns during the SELECT stage, WHERE comes before SELECT. The engine is trying to process the column by its alias before it’s been assigned. ORDER BY comes after SELECT so the same issue doesn’t arise here
It also explains why Intellisense doesn’t autocomplete or suggest column names until you’ve added the FROM statement. Until you tell the engine where the data is coming FROM, it doesn’t know which columns it can SELECT.
Extract Datasource and Query from Excel Pivot
Have you ever tried to reverse engineer an Excel pivot table? It’s not as easy as you would think! Whether you just want to find out the datasource details, or identify the query that was used, there is just no simple way of getting Excel to tell you.
The macro below will loop through every sheet in a workbook, and will document the datasources, SQL or MDX queries as well as the page, row, column and data fields.
To use it, add it into your macros, then select a starting cell where you want te report to be placed and run the macro. It’s pretty raw, and may need some tweaks to suit your requirements but it should give you a good starting point. I use it on Excel 2003 MDX pivots from SQL Server Analysis Services 2005, but I presume it will work on other versions of Excel as well.
Public Sub PivotDetails()
Dim ws As Worksheet
Dim qt As QueryTable
Dim pt As PivotTable
Dim pc As PivotCache
Dim pf As PivotField
For Each ws In ActiveWorkbook.Sheets
For Each qt In ws.QueryTables
ActiveCell.Value = "Sheet"
ActiveCell.Offset(0, 1).Value = ws.Name
ActiveCell.Offset(1, 0).Select
ActiveCell.Value = "Data Source"
ActiveCell.Offset(0, 1).Value = qt.Connection
ActiveCell.Offset(1, 0).Select
ActiveCell.Value = "Query"
ActiveCell.Offset(0, 1).Value = qt.CommandText
Next qt
ActiveCell.Offset(2, 0).Select
For Each pt In ws.PivotTables
ActiveCell.Offset(1, 0).Select
ActiveCell.Value = "Pivot Table"
ActiveCell.Offset(0, 1).Value = pt.Name
ActiveCell.Offset(1, 0).Select
ActiveCell.Value = "Connection"
ActiveCell.Offset(0, 1).Value = pt.PivotCache.Connection
ActiveCell.Offset(1, 0).Select
ActiveCell.Value = "SQL"
ActiveCell.Offset(0, 1).Value = pt.PivotCache.CommandText
ActiveCell.Offset(1, 0).Select
ActiveCell.Value = "MDX"
ActiveCell.Offset(0, 1).Value = pt.MDX
For Each pf In pt.PageFields
ActiveCell.Offset(1, 0).Select
ActiveCell.Value = "Page"
ActiveCell.Offset(0, 1).Value = pf.Name
ActiveCell.Offset(0, 2).Value = pf.CurrentPageName
Next pf
For Each pf In pt.ColumnFields
ActiveCell.Offset(1, 0).Select
ActiveCell.Value = "Column"
ActiveCell.Offset(0, 1).Value = pf.Name
Next pf
For Each pf In pt.RowFields
ActiveCell.Offset(1, 0).Select
ActiveCell.Value = "Row"
ActiveCell.Offset(0, 1).Value = pf.Name
Next pf
For Each pf In pt.DataFields
ActiveCell.Offset(1, 0).Select
ActiveCell.Value = "Data"
Next pf
Next pt
Next ws
End Sub