sp_TexasHoldEm: Multi-Player Poker in T-SQL
Wanna play some Texas Hold 'Em style poker and make a pile of Query Bucks? Wanna watch other database people playing?
Fire up SSMS and connect to:
Wanna play some Texas Hold 'Em style poker and make a pile of Query Bucks? Wanna watch other database people playing?
Fire up SSMS and connect to:
Common Table Expressions are awesome because they let SQL Server reorder processing in whatever way it deems to be the most efficient for your current data distribution, on your current version of SQL Server. Default to CTEs.
When SQL Server gets that process wrong, switch to temp tables.
For this month's T-SQL Tuesday, I invited y'all to tell me how you know a query's gonna be bad when you open it. Riccardo Perico and Shane O'Neill says WHILE loops and/or CURSORs are pretty good indicators. I agree, because especially when you're opening code in the year 2026, and someone still hasn't figured this out…
I open it up and the first thing I see is a comment with a changelog showing a long list of dates and author initials.
Oh I know, you think documentation is a good thing, but:
Way back in December of 2009, Adam Machanic published a blog post (archive) in which he invited database bloggers to participate in a new monthly event he called T-SQL Tuesday. Each month, he (or a blogger he picked) proposed a topic, and on that date, we'd all publish our blog posts at the same time. It was a fun way to get a variety of opinions about a topic.
Back in March 2025 when Microsoft first announced that REGEX support was coming to SQL Server 2025 and Azure SQL DB, I gave it a quick test, and the performance was horrific. It was bad in 3 different ways:
The CPU usage was terrible, burning 60 seconds of CPU time to check a few million rows
It refused to use an index
The cardinality estimation was terrible, hard-coded to 30% of the table
In last week's Query Exercise, I challenged you to play some code golf to generate big spills with tiny T-SQL.
Today, I'm going to walk you through my thought process - the initial attempts I tried and failed with, and the discoveries I made along the way, because I think it makes for fun storytelling.
When I was testing SQL Server 2025's new ability to limit TempDB usage with Resource Governor, I wrote a few fun diabolical demos. One of them was to generate giant spills to TempDB, quickly.
When you're looking at an actual (not estimated) query plan, and you see a yellow exclamation point on a sort operator, that means:
When I'm tuning queries, the normal answer is to make the query perform better - either via changing the T-SQL, adding hints, or adding indexes so that the data's better prepared for the query.
However, sometimes when I'm looking at the output of sp_BlitzCache, I scroll across to the Average Rows column and double-check that the query's actually returning a reasonable number of rows out in the wild.
In your most recent Query Exercise challenge, I gave you these two tables:
[crayon-6ab5c24c79bda216181453/]
I'mma be honest with you, dear reader: this query exercise stems from a client problem that I couldn't figure out how to solve in the time constraints that I had, and it drove me crazy. I wanted to put hours into this to figure it out, and I was sure there'd be a fun T-SQL way to do it.
For this week's Query Exercise, your mission had two parts:
Do a first pass search through the Users table looking for columns where people had snuck in email addresses where they didn't belong
Write a more high-quality query to identify exactly which rows we needed to handle
Regular expressions are a way of doing complex string searches. They can be really useful, but they have a reputation: they're hard to write, hard to read, and they're even harder to troubleshoot. Once you master 'em, though, they come in handy for very specific situations.
This post isn't about their complexity, though. This post is about Azure SQL DB & SQL Server 2025's regex performance.
Personally identifiable information (PII) is freakin' everywhere.
When companies first start looking to identify and lock down their data, they think it's going to be as easy as identifying common columns like EmailAddress, DateOfBirth, SocialSecurityNumber, and so forth. They think, "We'll just encrypt those columns and we'll be fine."
Let's say we have a couple of update statements we need to run every 15 minutes in the Stack Overflow database, and we've built indexes to support them:
[crayon-6ab5c24c7b051583440501/]
If you're using TRY/CATCH to do exception handling in T-SQL, you need to be aware that there are a lot of things it doesn't catch. Here's a quick example.
Let's set up two tables - bookmarks, and a process log to track whether our stored proc is working or not:
This week's Query Exercise challenged you to figure out why these 3 estimates went so badly:
[crayon-6ab5c24c7b737654630245/]
Our prior Query Exercise introduced SQL Server's 201 buckets problem: its inability to accurately estimate rows for more than 201 outliers in a table. I followed up with a solution using filtered statistics to help with the next 200 outliers, and I talked about how that's really overkill for the simple problem we were facing in that initial challenge.
In this week's Query Exercise challenge, I explained SQL Server's 201 buckets problem. SQL Server's statistics only handle up to ~201 outliers, which means that outliers ~202-300 get wildly inaccurate estimates.
In our example, I had an index on Location and perfectly accurate statistics, but even still, this query gets bad estimates because Lithuania is in outliers ~202-300:
When you run a query, SQL Server needs to estimate the number of matching rows it'll find - so that it can decide which indexes to use, whether to go parallel, how much memory to grant, and more.
For example, take any Stack Overflow database, and let's say I have an index on Location, and I want to find the top-ranking users in Lithuania: