2

I'm trying to display the last name of the lowest paid employees from each city. The city column falls under a table titled LOCATIONS while employee information(salary, last name) falls under EMPLOYEES. Both of these tables are related share no common table, so I have to rely on a third table, DEPARTMENTS to connect the two as DEPARTMENTS contains a department_id that it shares with EMPLOYEES as well as a LOCATION_ID that it shares with LOCATIONS. This is what I have so far, but I'm having trouble with this as I've mostly worked with only two tables in the past.

SELECT LAST_NAME
FROM EMPLOYEES
WHERE (DEPARTMENT_ID) IN
(SELECT DEPARTMENT_ID
FROM DEPARTMENTS
WHERE LOCATION_ID IN
(SELECT LOCATION_ID
FROM LOCATIONS
GROUP BY CITY
HAVING MIN(SALARY)));
Glaz
  • 109
  • 1
  • 3
  • 10

3 Answers3

2

This seems to be an assignment in an intro course in SQL. So let's assume you can't use analytic functions, match_recognize clause, etc. Just joins and aggregates.

In the subquery in the WHERE clause below, we compute the min salary for each city. We need to join all three tables for this. Then in the overall query we join the three tables again, and we use the subquery for an IN condition (a semi-join). The overall query looks like this:

select e.last_name
from   employees e join departments d 
                     on e.department_id = d.department_id
                   join locations   l
                     on d.location_id   = l.location_id
where  ( e.salary, l.city ) in 
       (
         select   min(salary), city
         from     employees e join departments d 
                                on e.department_id = d.department_id
                              join locations   l
                                on d.location_id   = l.location_id
         group by city
       )
;
0

You should separate out the concept of table joins from WHERE clauses. Use WHERE for filtering data, use JOIN for connecting data together.

I think this is what you are wanting. By the way, lose the ALL CAPS if you can.

SELECT
    LAST_NAME
FROM
    EMPLOYEES
    INNER JOIN (
        SELECT
            DEPARTMENTS.DEPARTMENT_ID, 
            CITY,
            MIN(SALARY) AS LOWEST_SALARY
        FROM
            EMPLOYEES
            INNER JOIN DEPARTMENTS ON EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID
            INNER JOIN LOCATIONS ON DEPARTMENTS.LOCATION_ID = LOCATIONS.LOCATION_ID
        GROUP BY
            DEPARTMENTS.DEPARTMENT_ID,
            LOCATIONS.CITY
    ) AS MINIMUM_SALARIES
    ON EMPLOYEES.DEPARTMENT_ID = MINIMUM_SALARIES.DEPARTMENT_ID
       AND EMPLOYEES.SALARY = MINIMUM_SALARIES.LOWEST_SALARY
Alan
  • 1,378
  • 2
  • 19
  • 24
  • The join on MINIMUM_SALARIES should be on "LOWEST_SALARY", since an alias is used. – Beltaine Oct 13 '17 at 20:34
  • All caps is standard in Oracle sadly. – Twelfth Oct 13 '17 at 20:36
  • 1
    This also can returns multiples lowest salaries by city since it is also grouped by `Department_ID`. This means you can receive multiple employees from a same city that equals their departement-city lowest salary. Just drop the `Department_ID` from the subquery and join the city table on the outside – Beltaine Oct 13 '17 at 20:36
  • 1
    @Twelfth - What are you talking about? Where in Oracle are all caps "standard"? –  Oct 13 '17 at 20:44
  • @mathguy - https://stackoverflow.com/questions/13342165/how-i-can-create-a-table-with-oracle-but-with-small-characters You only have to work in an Oracle schema that someone has forced to lower case once to know this pain...even worse is SomeOneConvertedAllToCamelCase – Twelfth Oct 13 '17 at 21:25
  • @Twelfth - OK, but what does that have to do with the question or with Alan's answer? Who ever mentioned anything about table or column names in double-quotes? –  Oct 13 '17 at 21:27
  • @mathguy Just replying to "By the way, lose the ALL CAPS if you can." – Twelfth Oct 13 '17 at 21:28
  • 1
    Oh, I see. Then I disagree. The only "standard" about all caps is that table and column names are converted to all caps in the data dictionary. There is nothing about capitalization regarding SQL code written for Oracle. (Indeed, if you will search at least my answers on this site, you will find they are all in all-lower-caps, and they are Oracle only, I don't know anything else). And as long as you don't use double-quotes anywhere, table and column names can be written in any capitalization. The advice to "lose the ALL-CAPS" is sound. –  Oct 13 '17 at 21:36
  • I like the advice you are giving in the first paragraph. Funny though, you are not following this advice in your query whereas mathguy does in his :-) – Thorsten Kettner Oct 14 '17 at 07:40
  • @Thorsten Kettner - I was following his coding. I felt nauseous writing it. – Alan Oct 14 '17 at 07:46
0

First of all join the tables, so you see city and employee in one row. If we group by city we get the minimum salary per city.

with city_employees as
(
  select l.city, e.*
  from locations l 
  join departments d using (location_id)
  join employees e using (department_id)
)
select last_name 
from city_employees 
where (city, salary) in
(
  select city, min(salary)
  from city_employees
  group by l.city
);

It is easier to achieve the same, however, with window functions (min over or rank over here).

select last_name
from
(
  select 
    e.last_name,
    e.salary,
    min(e.salary) over (partition by l.city) as min_salary
  from locations l 
  join departments d using (location_id)
  join employees e using (department_id)
)
where salary = min_salary;
Thorsten Kettner
  • 89,309
  • 7
  • 49
  • 73
  • Oops, I just notice this is the same answer as given by mathguy twenty minutes ago. Sorry. – Thorsten Kettner Oct 13 '17 at 21:20
  • lol - there is a little `delete` link under the answer, which can be used for such situations. –  Oct 13 '17 at 21:38
  • I see you used a WITH clause, and here it makes perfect sense since the same join is used twice. I though of doing the same, but I don't know how early in intro courses in SQL they introduce the WITH clause. –  Oct 13 '17 at 21:41