Pavlovian
There’s a strange response to some things in the SQL Server community that borders on religious fervor. I once worked with someone who chastised me for having SELECT * in some places in the Blitz scripts. It was odd and awkward.
Odd because this person was the most Senior DBA in the company, and awkward because they didn’t believe me that it didn’t matter in some cases.
People care about SELECT * for many valid reasons, but context is everything.
One For The Money
The first place it doesn’t matter is EXISTS. Take this index and this query:
CREATE INDEX specatular_blob ON dbo.Posts(PostTypeId, OwnerUserId); SELECT COUNT(*) AS records FROM dbo.Users AS u WHERE EXISTS ( SELECT * FROM dbo.Posts AS p WHERE p.OwnerUserId = u.Id AND p.PostTypeId = 2 );
The relevant part of the query plan looks like this:
We do a seek into the index we created on the two columns in our WHERE clause. We didn’t have to go back to the clustered index for everything else in the table.
That’s easy enough to prove if we only run the subquery — we have to change it a little bit, but the plan tells us what we need.
SELECT * FROM dbo.Posts AS p WHERE p.OwnerUserId = 22656 AND p.PostTypeId = 2;
This time we do need the clustered index:
You can even change it to something that would normally throw an error:
SELECT COUNT(*) AS records FROM dbo.Users AS u WHERE EXISTS ( SELECT 1/0 FROM dbo.Posts AS p WHERE p.OwnerUserId = u.Id AND p.PostTypeId = 2 );
Two For Completeness
Another example is in derived tables, joins, and apply.
Take these two queries. The first one only selects columns in our nonclustered index (same as above).
The second one actually does a SELECT *.
/*selective*/ SELECT u.Id, u.DisplayName, ca.OwnerUserId, --I am only selecting columns in our index ca.PostTypeId, ca.Id FROM dbo.Users AS u CROSS APPLY( SELECT TOP (1) * --I am select * FROM dbo.Posts AS p WHERE p.OwnerUserId = u.Id AND p.PostTypeId = 2 ORDER BY p.OwnerUserId DESC, p.Id DESC) AS ca WHERE U.Reputation >= 100000; /*less so*/ SELECT u.Id, u.DisplayName, ca.* --I am select * FROM dbo.Users AS u CROSS APPLY( SELECT TOP (1) * --I am select * FROM dbo.Posts AS p WHERE p.OwnerUserId = u.Id AND p.PostTypeId = 2 ORDER BY p.OwnerUserId DESC, p.Id DESC) AS ca WHERE U.Reputation >= 100000;
The first query only touches our narrow nonclustered index:
The second query does a key lookup, because we really do select everything.
Trash Pile
I know, you’ve been well-conditioned to freak out about certain things. I’m here to help.
Not every SELECT * needs to be served a stake through the heart and beheading.
Thanks for reading!
Going Further
If this is the kind of SQL Server stuff you love learning about, you’ll love my training. I’m offering a 75% discount to my blog readers if you click from here. I’m also available for consulting if you just don’t have time for that and need to solve performance problems quickly.
Related Posts
- SQL Server 2017 CU 30: The Real Story With SelOnSeqPrj Fixes
- Getting The Top Value Per Group With Multiple Conditions In SQL Server: Row Number vs. Cross Apply With MAX
- Getting The Top Value Per Group In SQL Server: Row Number vs. Cross Apply Performance
- Multiple Distinct Aggregates: Still Harm Performance Without Batch Mode In SQL Server
Nice post, Eric. Should the following be forbidden as an evil ‘SELECT *”?
SELECT TOP 0 * INTO temp_thetable FROM thetable;
Only so far as you may not need every column in the temp table.
But I’m fine with that or
SELECT * INTO #t WHERE 1 = 0;
too.Thanks!
Erik,
Thanks for the edumacation! I are gettin’ smarter by the minute!
Although you pointed out cases where SELECT * doesn’t matter, but I’m not sure if that means it’s ever a good idea.
Is SELECT * actually advantageous in any of these (or other) examples? And if not, shouldn’t you avoid using it just to make it easier for your colleagues (or future you) to review your scripts without having to reevaluate whether or not it’s sacrilegious?
Heh, well, that’s the thing: I write these posts so people don’t have to puzzle over’em.
THANK YOU!
Hopefully you receive this before you get the visit from the Thought Police.
One possible situation where SELECT * is recommend best practice: in an archiving situation, where rows are being moved to another table that must have the same structure. More details here https://dba.stackexchange.com/a/253917/107045
I wondered why you kept using “Select 1/0 …” I thought, one OR zero?
It’s a binary choice, heh heh heh