From https://learn.microsoft.com/en-us/sql/t-sql/queries/select-group-by-transact-sql?view=sql-server-ver16 it says "However, each table or view column in any nonaggregate expression in the list must be included in the GROUP BY list". Exactly as you'd expect (there seem to be a couple of exceptions, probably MSSQL specific, regarding string concatenation but they don't seem to be relevant here).
At work I intended to write this:
SELECT
max(STATS_DATE(object_id, stats_id)),
object_name(object_id)
FROM sys.stats
GROUP BY object_name(object_id)
Which works as expected. What I actually wrote by accident was this
SELECT
max(STATS_DATE(object_id, stats_id)),
object_name(object_id)
FROM sys.stats
GROUP BY object_id -- should have been object_name(object_id)
The SELECT contains object_name(object_id) but the GROUP BY contains object_id, which is definitely not in the SELECT. ISTM this should have been rejected, but it runs fine.
I don't get it. Is there some support for functional dependencies that I'm not aware of? Is there even a functional dependency between object_id and object_name(object_id) (I don't think so because object_name(object_id)might not necessarily be unique, although my understanding of FDs is pretty poor).
What's going on?
With
you get one result row per object_id, say IDs 1, 2, 3 and 4.
With
you get their object names, e.g. 'Object 1', 'Object 2', 'Object 3', 'Object 4'.
As each result row refers to one object ID, you get the name for that object ID in your result row. Nothing wrong with this.
It would be different with
Here you'd group by the object name of the object IDs. Two or more IDs could have the same name. It is not possible hence to select the object ID, because it is not known and not necessarily unique per object name.
But this is not the case in your query.