tags:

views:

106

answers:

4

Why are column ordinals legal for ORDER BY but not for GROUP BY? That is, can anyone tell me why this query

SELECT OrgUnitID, COUNT(*) FROM Employee AS e GROUP BY OrgUnitID

cannot be written as

SELECT OrgUnitID, COUNT(*) FROM Employee AS e GROUP BY 1

When it's perfectly legal to write a query like

SELECT OrgUnitID FROM Employee AS e ORDER BY 1

?

I'm really wondering if there's something subtle about the relational calculus, or something, that would prevent the grouping from working right.

The thing is, my example is pretty trivial. It's common that the column that I want to group by is actually a calculation, and having to repeat the exact same calculation in the GROUP BY is (a) annoying and (b) makes errors during maintenance much more likely. Here's a simple example:

SELECT DATEPART(YEAR,LastSeenOn), COUNT(*)
    FROM Employee AS e
    GROUP BY DATEPART(YEAR,LastSeenOn)

I would think that SQL's rule of normalize to only represent data once in the database ought to extend to code as well. I'd want to only right that calculation expression once (in the SELECT column list), and be able to refer to it by ordinal in the GROUP BY.

Clarification: I'm specifically working on SQL Server 2008, but I wonder about an overall answer nonetheless.

+1  A: 

use aliasses :

SELECT DATEPART(YEAR,LastSeenOn) as 'seen_year', COUNT(*) as 'count'
    FROM Employee AS e
    GROUP BY 'seen_year'

** EDIT **

if GROUP BY alias is not allowed for you, here's a solution / workaround:

SELECT seen_year
     , COUNT(*) AS Total 
  FROM (
    SELECT DATEPART(YEAR,LastSeenOn) as seen_year, *
    FROM Employee AS e
  ) AS inline_view
GROUP 
    BY seen_year
Yanick Rochon
doesn't work in SQL Server.... Each GROUP BY expression must contain at least one column that is not an outer reference.
SQLMenace
Your edit would certainly work. As I'm in SQL Server 2008, I'd probably write it in a CTE to be cleaner. There's no doubt that there's a way around the problem. I'm really hoping to expand my understanding of SQL by seeing if there's a real reason for the limitation.
Chris Wuestefeld
+2  A: 

One of the reasons is because ORDER BY is the last thing that runs in a SQL Query, here is the order of operations

  1. FROM clause
  2. WHERE clause
  3. GROUP BY clause
  4. HAVING clause
  5. SELECT clause
  6. ORDER BY clause

so once you have the columns from the SELECT clause you can use ordinal positioning

EDIT, added this based on the comment Take this for example

create table test (a int, b int)
insert test values(1,2)
go

The query below will parse without a problem, it won't run

select a as b, b as a
     from test
    order by 6

here is the error

Msg 108, Level 16, State 1, Line 3
The ORDER BY position number 6 is out of range of the number of items in the select list.

This also parses fine

select a as b, b as a
     from test
    group by 1

But it blows up with this error

Msg 164, Level 15, State 1, Line 3
Each GROUP BY expression must contain at least one column that is not an outer reference.

SQLMenace
But what you're saying relates to the actual *execution* of the query. As far as I can see, an ordinal could be resolved by the interpreter at the time it parses the query, before anything has actually been executed.
Chris Wuestefeld
added code that parses fine but won't run
SQLMenace
I don't think that your example is correct. The code is syntactically correct, so technically speaking, it does go through the parsing stage successfully. I don't know how to check this, but I'm willing to bet that in both of your error examples, the query never actually executes as such -- no data or index pages are examined. The query optimizer can trivially see that the ORDER example does not have 6 columns. The GROUP example will fail even sooner than that, since the query is illegal given the SELECT clause.
Chris Wuestefeld
+1  A: 

I'm not sure if the standard specifies if it is valid, but I believe it is implementation-dependent. I just tried your first example with one SQL engine, and it worked fine.

Mark Wilkins
+1  A: 
Tegiri Nenashi