What does SQL Server actually see when it parses your code? Not the query plan, not the execution - the step before that. The raw parse tree. I built a web tool to make that visible.
What is an abstract syntax tree?
Before SQL Server can optimize or execute anything, it has to parse the text into a structured tree. Microsoft ships a .NET library called ScriptDOM that does exactly this: it takes a string of TSQL and returns an abstract syntax tree (AST) where every keyword, identifier, expression, and clause is a typed node.
If you've ever written or read anything built on top of ScriptDOM - a linter, a formatter, a migration analyzer - you've dealt with this tree. The problem is that it's invisible. You write SELECT * FROM dbo.Orders and the parser quietly turns it into a SelectStatement containing a QuerySpecification containing a SchemaObjectName with dbo in the schema slot and Orders in the base identifier slot. None of that is obvious from the flat text.
I've used the ScriptDOM library before, for instance to build a homegrown linter. You can also use it to build your own formatter with the correct set of rules.
Surprising parses
The best way to understand the AST is to throw weird SQL at it and see what happens. Here are a few that might surprise you.
Spaces where they shouldn't be
Brent Ozar showed this one in his Stupid T-SQL Tricks post. This is valid TSQL:
SELECT *
FROM sys . databases
Spaces around the dot? Sure, why not. The parser doesn't care about whitespace between the parts of a multi-part name. It still resolves sys as the schema and databases as the object, exactly the same tree as sys.databases. Paste both versions into the Visualizer and compare the trees - they're identical.
Reserved words as identifiers
This is also valid:
SELECT [SELECT]
FROM [FROM]
WHERE [WHERE] = 1
Square brackets let you use anything as an identifier. The AST shows these as regular ColumnReferenceExpression and SchemaObjectName nodes, no different from normal column and table names. The parser doesn't know or care that the identifier text happens to be a keyword.
Multi-part name slots
How many parts can an object name have? Try this:
SELECT *
FROM ServerName.DatabaseName.SchemaName.TableName
The SchemaObjectName node has four slots: ServerIdentifier, DatabaseIdentifier, SchemaIdentifier, and BaseIdentifier. When you write just FROM Orders, three of those slots are empty. When you write FROM dbo.Orders, two are empty. The Visualizer makes it obvious which slot each part fills.
Try it out
Head over to TSQL Visualizer and paste in some TSQL. The tool has three panels - Editor, Tree, and Fragment - with a three-way sync: click a token in any panel and it highlights the corresponding position in the other two.

Try pasting your own queries and clicking around. Some things worth exploring: how CASE expressions nest, what a subquery looks like in the tree, or how a JOIN clause is structured compared to a comma-separated FROM.
About the project
Full transparency: this tool is a vibe-coded, AI-assisted project. I'm learning C# and Blazor, and building something useful seemed like a better way to learn than following tutorials. The code is probably not production-grade, but it works and hopefully helps others understand ScriptDOM.
Thank you for reading