My database is for a school project and handles various jobs/contracts which are ran by consultants. Apologies if the context is vague, we haven’t really been given much either.
My current code does what it is suppose to, it selects the forename and surname of the consultant which has led the most contracts/jobs. However I need to display the result in a specific format.
SELECT DISTINCT CONSULTANT_FNAME, CONSULTANT_SNAME FROM CONSULTANT LEFT JOIN CONTRACT ON CONSULTANT_ID = CONTRACT_LEAD WHERE CONTRACT_LEAD IN( SELECT CONTRACT_LEAD FROM CONTRACT GROUP BY CONTRACT_LEAD ORDER BY COUNT(CONTRACT_LEAD) DESC FETCH NEXT 1 ROWS ONLY);
Currently the result I am getting is:
CONSULTANT_FNAME CONSULTANT_SNAME -------------------- -------------------- Cloud Strife
However I need to display the result as “The consultant who has led the most contracts is: Cloud Strife”.
Advertisement
Answer
You shouldn’t need DISTINCT – you only put it in there because the LEFT JOIN is causing the consultant rows to repeat (multiple contracts per one consultant) but you don’t need the left join either
SELECT 'The consultant who has led the most contracts is: ' || CONSULTANT_FNAME || ' ' || CONSULTANT_SNAME FROM CONSULTANT WHERE CONSULTANT_ID IN( SELECT CONTRACT_LEAD FROM CONTRACT GROUP BY CONTRACT_LEAD ORDER BY COUNT(CONTRACT_LEAD) DESC FETCH NEXT 1 ROWS ONLY )
The subquery inside the IN returns the most active contract lead id, but this is the same thing as the consultant id – you don’t need to join.
I believe you could also do this:
SELECT 'The consultant who has led the most contracts is: ' || CONSULTANT_FNAME || ' ' || CONSULTANT_SNAME FROM CONSULTANT INNER JOIN CONTRACT ON CONSULTANT_ID = CONTRACT_LEAD GROUP BY CONSULTANT_FNAME, CONSULTANT_SNAME ORDER BY COUNT(*) DESC FETCH NEXT 1 ROWS ONLY
The reason why this works is that grouping on name is effectively the same as grouping on id : it gives a count of how many times the contract table has caused a repeat. There is a slight bug risk with this though that if there really were two different people of identical names they would have their contracts added together. To counter this you can add consultant_id to the group by – if you’re doing a group by then you cannot use columns in the SELECT area that are not mentioned in the group by (unless you put them inside an aggregate function like min, max etc)but you ARE allowed to put columns in the group by that you then don’t use in the select. By adding the ID We provide a way to tell two identically names consultants from having their scores added together:
SELECT 'The consultant who has led the most contracts is: ' || s.CONSULTANT_FNAME || ' ' || s.CONSULTANT_SNAME FROM CONSULTANT s INNER JOIN CONTRACT t ON s.CONSULTANT_ID = t.CONTRACT_LEAD GROUP BY s.CONSULTANT_FNAME, s.CONSULTANT_SNAME, s.CONSULTANT_ID ORDER BY COUNT(*) DESC FETCH NEXT 1 ROWS ONLY
The final learning point here; always alias your tables (I used s and t) and use the aliases. This prevents your queries from stopping working if someone adds a new column to eg contracts also called CONSULTANT_FNAME – without the alias your query will only work if column names are unambiguous, and db schema do change over time
Adding table aliases also lets you join one table in twice, which can be vital if e.g. a consultant has a home_address_id and also a work_address_id that map to two different rows in the addresses table. If you joined addresses in only once using home_address_id = address.id OR work_address_id = address.id
you’d end up with two rows when the data would be more usable as one row. Here ends sql 101 🙂 but if you have more questions about this nugget of info see something like When to use SQL Table Alias