views:

170

answers:

5

To simplify, if I had a table called 'employee':

id INT NOT NULL PRIMARY KEY,
salary FLOAT,
department VARCHAR(255)

I want to perform and query where I retrieve the minimum salary in each department. So my query is something like this:

SELECT employee.ID, MIN(employee.salary), employee.department
FROM employee
GROUP BY employee.department

But regardless of which records are found. The ID values in the result set are renamed to 1,2,3.... up to however many records (deparments) exist in the result set.

How can I maintain the actual ID's of the employees after performing the AGGREGATE function and GROUP BY? Thanks in advance.

A: 

This will do the trick:

SELECT employee.department, MIN(employee.salary), employee.ID
FROM employee
GROUP BY 1
Am
A: 

Your script is invalid:

SELECT employee.ID, MIN(employee.salary), employee.department
FROM employee
GROUP BY employee.department

Instead, look at this:

SELECT MIN(employee.salary), employee.department
FROM employee
GROUP BY employee.department

If you need the employee id as well, then you need to use a subquery.

Gabriel McAdams
+2  A: 

You can't. Think about it, If a Department has 20 employees, and for that department, there are three employees that have the same minimum salary, which EmployeeId do you want the the query output to display? if it was guaranteed that there was only one employee in each dept with that lowest salary, then it can be done by selecting the specific employee records where the salary is the minimum value for each Department:

Select EmployeeID
From Employee e
Where Salary = 
      (Select Min(Salary) From EMployee
       Where DepartmentId = e.DepartmentId)

but this will return multiple records per department when more than one employee has that min salary level.

Charles Bretana
i disagree, i tested my offer and it works
Am
@Am, I looked at yr sql,.. I confess I am not familiar with that syntax, What does `Group By 1` mean? Are you grouping by the first ordinal attribute in the table? or by the constant value "!" ?
Charles Bretana
it's grouping by the ordinal values
Am
on second though (and test), the field type nor the order makes any different. (including the PO's code), so nvm. guess it depends on the server type...
Am
@Am, I'm guessing, cause I don't know what your database works, but in standard SQL, if it worked at all, that just groups by a constant value (integer 1) which means you shoulf only be getting one row, with the minium salary for all departments, not for each department...
Charles Bretana
@Charles: I'm using mysql, and I'm sure it is ordinal order of the fields in the select. (since i'm getting more then 1 row)
Am
@Am, I'll rely on you, (and if you're getting more than one row, than you must be grouping by the first ordinal attribute , as you said), but as the OP defines it, that first attribute is the EmployeeId, which means you should be gettingt one row, (and the min(Salary) for each employee, not one row (and min(Salary) per department
Charles Bretana
A: 

In modern SQL Server releases (and other reasonably powerful and modern SQL engines), SQL "Window functions" are probably the best alternatives (to be preferred over subqueries and self-joins) to do what you desire:

  SELECT ID, salary, department
  FROM employee
  WHERE 1 = ROW_NUMBER() OVER(PARTITION BY department ORDER BY salary ASC)

This works when, if multiple employees have the same (department-minimal) salary, you want just a "random-ish" one of them (you can add criteria to the ORDER BY if you want one picked by some specific criteria); look into RANK, instead of ROW_NUMBER, if you want all.

Alex Martelli
Alas, MySQL doesn't support `ROW_NUMBER()` currently.
Bill Karwin
@Bill, that's right -- I did specify "reasonably powerful and modern";-). I don't see any indication from the OP that he's using old or non-standard SQL implementations (or, which ones, if he is), so I gave the standard-SQL answer (shd work on PostgreSQL, SQL Server, Oracle, IBM DB2, etc, etc).
Alex Martelli
Yeah I just researched this for my book-in-progress. Window functions are now supported by most commercial RDBMS (except Informix and InterBase). Among open-source vendors, PostgreSQL 8.4 and Apache Derby 10.4 support window functions; not MySQL, SQLite, Firebird, etc.
Bill Karwin
A: 

I would guess you're using MySQL or SQLite, because your query is ambiguous and isn't allowed by standard SQL or most brands of RDBMS. MySQL and SQLite are more permissive, so it's your responsibility to resolve the ambiguity.

Here's my usual fix:

SELECT e1.ID, e1.salary, e1.department
FROM employee e1
LEFT OUTER JOIN employee e2 ON (e1.department = e2.department
    AND e1.salary > e2.salary)
WHERE e2.department IS NULL;

Here's another solution that gives the same result:

SELECT e1.ID, e1.salary, e1.department
FROM employee e1
JOIN (SELECT e2.department, MIN(e2.salary) AS min_salary
      FROM employee e2 GROUP BY e2.department) d
  ON (e1.salary = d.min_salary);

Both of these give multiple rows per department if there are multiple employees in the department with identical minimal salaries. You need to decide how to resolve that case, because it's not clear from your problem description.

Bill Karwin