It depends on the table and indexing layout, and the database engine you are using, but generally there are two reasons for the COUNT
example to be faster:
1. Less pages need to be read. After finding which rows need to be returned due to the results of the join and filter operations, the query may still need to read data pages to find the values for the extra columns you want to output.
This is not always the case: if the table design and query are such that everything is being table-scanned anyway the two queries will likely perform equally badly. Also in DBs that support clustered indexes if the query planner ends up using only the clustering keys there probably won't be further reads to do. Also you may have covering indexes which have a similar effects on how much work is needed to find all the desired output information.
Using examples with SQL Server's query plans from one of our DBs (other DBs will behave similarly): SELECT JobTitleId FROM Person WHERE JobTitleId = 17
produces
showing that it just needed to look at the simple index to find all it needed. The plan for SELECT JobTitleId, LastName, FirstName FROM Person WHERE JobTitleId = 17
shows the extra lookups needed for the extra output columns:
The plan for SELECT COUNT(*) FROM Person WHERE JobTitleId = 17
shows a little extra work needs to be done to actually count the rows, but this is very small compared to having to go away and read extra data pages:
You can see the number of pages touched in each case with SET STATISTICS IO ON
: both the single column and count examples show logical reads 2
and the one with extra columns output shows logical reads 213
.
2. Less data needs to be transferred and processed. This is often less significant than the above, though not always for large data and/or slow connections. Once the engine has find the data it needs to send it to the calling application (possibly over a network rather than via a local IPC response) then the application needs to process and display it. Your COUNT
query will only ever result in one row with one value so this will be quick, the other query could result in a great many rows being transferred to, and processed by, the application.
NOTE: I've used simpler queries than your examples in my explanation, but the considerations are the same for more complex queries with joins, sub-queries, and other extra work, just potentially multiplied by the number of objects in play.