Problems using GROUP BY and ORDER BY in SQL Oracle - oracle

So I've been instructed to write a SQL query to display the manager number and the salary of the lowest paid employee for that manager(2 columns within a database I'm working in). I must also exclude any groups where the minimum
salary is less than or equal to $6,000. Sort the output in descending order of
salary. Now I've successfully displayed the data however, I cannot seem to order it in descending order which is what brought me here. Currently my code is as follows:
Code Snippet #1
SELECT manager_id AS "Manager ID",
MIN(salary) AS "Lowest Paid Salary"
FROM employees
WHERE manager_id IS NOT NULL
AND salary > 6000
GROUP BY manager_id;
Which gives me the following output:
Code Snippet #2
Manager ID Lowest Paid Salary
---------- ------------------
100 6500
147 6200
205 8300
108 6900
148 6100
149 6200
102 9000
101 6500
145 7000
146 7000
However, I need these values ordered from Largest -> Smallest salary. I attempted to add a GROUP BY salary DESC; At the end of the statement which would give me the following error: ORA-00979: not a GROUP BY expression
Upon some research I found that the two (GROUP BY and ORDER BY) cannot be used together in the same query as I did not include both manager_id and salary within the GROUP BY clause. So I attempted that with the following code:
SELECT manager_id AS "Manager ID",
MIN(salary) AS "Lowest Paid Salary"
FROM employees
WHERE manager_id IS NOT NULL
AND salary > 6000
GROUP BY manager_id, salary
ORDER BY salary DESC;
Which gives me a list of all the salaries for each manager_id as well as duplicates. This is not what I am looking for.
I apologize for the excess of reading, I just want to make sure you know what I've tried and what I am attempting to accomplish.
So I need to know a way I can either use both the GROUP BY and ORDER BY in the same query to display the Code snippet #2 but have it ordered. Or another way to order this data in the same format.
Thanks in advance for any help/advice.

You can either sort by the column number or column name
SELECT manager_id AS "Manager ID",
MIN(salary) AS "Lowest Paid Salary"
FROM #employees
WHERE manager_id IS NOT NULL
AND salary > 6000
GROUP BY manager_id
order by 2 desc
SELECT manager_id AS "Manager ID",
MIN(salary) AS "Lowest Paid Salary"
FROM #employees
WHERE manager_id IS NOT NULL
AND salary > 6000
GROUP BY manager_id
order by "Lowest Paid Salary" desc

You need to order by the aggregate function. ...
Try this
SELECT manager_id AS "Manager ID",
MIN(salary) AS "Lowest Paid Salary"
FROM employees
WHERE manager_id IS NOT NULL
AND salary > 6000
GROUP BY manager_id
ORDER BY min(salary) DESC;

Try:
order by manager_id ,salary DESC and remove the group by clause
for example
SELECT * FROM Customers
ORDER BY CustomerID DESC,CustomerName;
This statement will arrange the results according to customer id in desc order.

Related

Finding list of max average salary, department wise, whilst displaying both max average salary and department_id

When I try it without displaying department_id it works fine as :
SQL> SELECT MAX(AVG(SALARY)) FROM EMPLOYEE GROUP BY DEPARTMENT_ID;
MAX(AVG(SALARY))
----------------
800000
But when I want to display the department_id's too, it gives me error as follows:
SQL> SELECT DEPARTMENT_ID, MAX(AVG(SALARY)) FROM EMPLOYEE GROUP BY DEPARTMENT_ID;
SELECT DEPARTMENT_ID, MAX(AVG(SALARY)) FROM EMPLOYEE GROUP BY DEPARTMENT_ID
*
ERROR at line 1:
ORA-00937: not a single-group group function
Is there any explanation for this? What am I doing wrong? I went through answers of previous questions like this and tried their solutions but got same or some other error. Any help would be appreciated.
I suggest you use
SELECT department_id, avg_salary
FROM ( SELECT DEPARTMENT_ID,
AVG (SALARY) avg_salary,
RANK () OVER (ORDER BY AVG (salary) DESC) rnk
FROM EMPLOYEE
GROUP BY DEPARTMENT_ID)
WHERE rnk = 1;
i.e.
use your first query as a "source"
additionally, rank average salaries in descending order (using the rank analytic function)
select row which ranks as highest

Problem with MINUS and sub queries with ORDER BYs

select salary
from (
(select salary
from employees
where rownum<=10
order by salary desc)
minus
(select salary
from employees
where rownum<=4
order by salary desc)
);
You cannot use ORDER BY there.
Try this instead:
select salary from (
select salary, row_number() over ( order by salary desc ) rn
from employees )
where rn between 5 and 10;
On Oracle 12c or later, you can also do this:
select salary from employees
order by salary desc
offset 4 rows fetch next 6 rows only;
You've got several issues in what you've written. The immediate problem is that you'll get an error from having an order by in the first branch of your union, but just removing that won't help you much.
You're making a (fairly common) mistake with ordering and rownum; looking just at the first subquery you have:
select salary
from employees
where rownum<=10
order by salary desc
The rownum filter will be applied before the order-by, so what this will actually produce is 10 indeterminate rows from the table, which are then ordered. If I run that I get:
SALARY
----------
24000
13000
12000
10000
8300
6500
6000
4400
2600
2600
but you'll see different values, even from the same sample schema. If you look at the whole table you'll see higher values than those; and even running the second query will show something isn't as you expect - for me that gets:
SALARY
----------
13000
4400
2600
2600
which are not the first four rows from the previous query. (Again, you'll see different results, but hopefully the same effect; if not, look at the whole table ordered by salary.)
You need to order the whole table - in a subquery - and then filter:
select salary
from (
select salary
from employees
order by salary desc
)
where rownum<=10
which gives a much more sensible - and consistent - result. You can then minus the two queries:
select salary
from (
select salary
from employees
order by salary desc
)
where rownum<=10
minus
select salary
from (
select salary
from employees
order by salary desc
)
where rownum<=4
order by salary desc;
SALARY
----------
13500
13000
12000
11500
You may be expecting to see six values there, but there are three employees with a salary of 12000, and minus eliminates duplicates so that is only reported once. #Matthew's approach (or #Jeff's!) will give you all six, including duplicates, if that is what you want. It also stops you having to hit the table multiple times.
A further problem is with ties - if the 4th highest was the same as the 5th highest, what would you expect to happen? Using minus would exclude that value; #Matthew's approach would preserve it.
You need to define what you actually want to get - the 5th to 10th highest salary values? The salaries of the 5th to 10th highest-paid people (a subtle but important difference)? Do you really only want the numbers, or who those employees are - in which case how you deal with ties is even more important? Etc. Once you know what you actually need to find you can decide the best way to get that result.
It doesn't make sense to order rows in two sets that are subsequently operated upon because sets don't have order. If you need a solution that can execute on older versions and you want to return the bottom 6 ranked out of the top 10 ranked, then this will work. If you can use newer features, then you may want to because it's possible they'll require fewer machine instruction executions.
After making the obvious changes that escaped me in my haste...
select salary
from (
select rownum rn, salary
from (
select salary
from employees
order by salary desc
)
)
where rn between 5 and 10

Find second highest salary in each department using rank/dense_rank in hive

These were the two questions asked to me during an interview but only condition is to use rank/dense_rank.
Find second highest salary in each department using rank/dense_rank in
hive.
When there are sufficient records in each department.
When there is only 1 record in few departments.
Guide me if this is even valid or not.
If yes then what should be the queries for the same.
If you need to select all second salary employees then use
dense_rank() over(partition by department order by salary desc) = 2 filter. It will return all employees with second salary.
When there is only 1 record in the department (no second salary exists, one employee in the department), it will be ranked 1 and you will get no records for that department filtering by dense_rank = 2.
If you need to select only one record (not all employees) with second salary then row_number() over(partition by department order by salary desc) = 2 will work, but it will pick one random employee with second salary if there are many employees with second salary. Only one record will be marked with row_number = 2.
Try this:
WITH RESULT AS
(
SELECT salary, DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS SecondHighest FROM Employee
)
SELECT IFNULL(( SELECT salary FROM RESULT WHERE SecondHighest = 2 ORDER BY salary) , NULL) as SecondHighestSalary

display manager name and count of employees reporting him in employees table

I want to display manager_name and count of employees reporting him in employees table.I want to sort the data based on count IE maximum employees reporting to a manager should come first.
I tried to write self join but i could not get the out put .
EMPLOYEE_ID FIRST_NAME MANAGER_ID SALARY HIRE_DATE
198 Donald 124 2600 21-JUN-99
199 Douglas 124 2600 13-JAN-00
200 Jennifer 101 4400 17-SEP-87
201 Michael 100 13000 17-FEB-96
202 Pat 201 6000 17-AUG-97
203 Susan 101 6500 07-JUN-94
204 Hermann 101 10000 07-JUN-94
205 Shelley 101 12000 07-JUN-94
206 William 205 8300 07-JUN-94
100 Steven 24000 17-JUN-87
101 Neena 100 17000 21-SEP-89
the table name is employees and i want to see names also
You can use the aggregate function COUNT and ORDER BY clause
You didn't mention the table name assuming the table name as EMPLOYEES, below query would help you.
SELECT MANAGER_ID, COUNT(EMPLOYEE_ID) as EMP_COUNT
FROM EMPLOYEES
GROUP BY MANAGER_ID
ORDER BY EMP_COUNT DESC;
Here EMP_COUNT is the column alias name.If you don't want any column alias you can simply use the query below.
SELECT MANAGER_ID, COUNT(EMPLOYEE_ID)
FROM EMPLOYEES
GROUP BY MANAGER_ID
ORDER BY COUNT(EMPLOYEE_ID) DESC;
If you want to sort by ascending order instead of DESC you can use ASC.
We can get this output using an analytical function:
SELECT E.EMPID,E.EMPNAME as "Manager Name",M.EMPNAME AS "Employee Name",count(*) over(partition by e.empid) reportee_count
from empmgid m,empmgid e where M.MAGID=e.EMPID order by reportee_count desc;
Please employ the following SQL-Query:
SELECT
e.empno,
e.ename,
e1.empcnt
FROM
emp e,
(
SELECT
mgr,
COUNT(*) empcnt
FROM
emp
GROUP BY
mgr
) e1
WHERE
e.empno = e1.mgr;
-- Restricting which manager is having two employees working under them
-----------------------------------------------------------------------
SELECT E1.* FROM
(
SELECT E1.EMPNO,E1.ENAME AS EMPLOYE,
M1.ENAME AS MANAGERS,
COUNT(*)
OVER
(
PARTITION BY E1.EMPNO
) EMPCNT
FROM EMP E1,EMP M1
WHERE M1.MGR=E1.EMPNO
) E1
WHERE EMPCNT = 2;
select count(distinct manager_id) from employees;
select count(distinct manager_id) from employees;
Ans:
COUNT(DISTINCTMANAGER_ID)
18

ORACLE: multiple column subqueries

This is from Oracle Database 10g: SQL Fundamentals.
Original Oracle textbook.
Francly speaking, this all is actual so far.
There is a task which troubles me:
Display the last name, department name, and salary of any employee whose salary and commission match the salary and commission of many employee located in location ID 1700.
The topic to be learned is multiple column subqueries. This mean that we can't deviate from the offered model which is:
SELECT column, column, ...
FROM table
WHERE (column, column, ...) IN
(SELECT column, column, ...
FROM table
WHERE condition);
Well, my database:
describe employees
Name Null Type
-------------- -------- ------------
EMPLOYEE_ID NOT NULL NUMBER(6)
FIRST_NAME VARCHAR2(20)
LAST_NAME NOT NULL VARCHAR2(25)
EMAIL NOT NULL VARCHAR2(25)
PHONE_NUMBER VARCHAR2(20)
HIRE_DATE NOT NULL DATE
JOB_ID NOT NULL VARCHAR2(10)
SALARY NUMBER(8,2)
COMMISSION_PCT NUMBER(2,2)
MANAGER_ID NUMBER(6)
DEPARTMENT_ID NUMBER(4)
describe departments
Name Null Type
--------------- -------- ------------
DEPARTMENT_ID NOT NULL NUMBER(4)
DEPARTMENT_NAME NOT NULL VARCHAR2(30)
MANAGER_ID NUMBER(6)
LOCATION_ID NUMBER(4)
There is a solution:
select
e.last_name,
d.department_name,
e.salary
from employees e, departments d
where e.department_id = d.department_id
and (salary, nvl(commission_pct, 0)) in
(select salary, nvl(commission_pct, 0)
from employees e, departments d
where e.department_id = d.department_id
and d.location_id = 1700);
SELECTED 36 ROWS.
Well, I started checking. It seems that employees from location 1700 duplicated against themselves.
Look: we take the whole list of employees and then juxtapose it with employees from location 1700. Of course, there will be duplicates.
I have prepared an excel file with some data (all employees, employees from location 1700, the result of my own desired result etc.). In the file it is more picturesque, with examples and so on. Please, have a look.
This is the file: https://skydrive.live.com/redir?resid=8CDFFBA921B002FE!150&authkey=!ADMRAE466BIunQM
Well, what I would like to do is to control that no employee is compared with themselves.
This was my variant bevore I checked the solution.
select lnme, dpt, slry
from
(
select
employee_id eid,
last_name lnme,
salary slry,
nvl(commission_pct,0) cpct,
d.department_name dpt,
location_id
from employees e
left join departments d
on e.department_id = d.department_id
)
where (slry, cpct) in
(select
employee_id ide,
salary slry,
nvl(commission_pct,0) cpct
from employees e
join departments d
on e.department_id = d.department_id and location_id = 1700)
and ide <> eid
I wanted to make sure that no employee is compared with the same employee. But I failed.
Could you comment on all this situation:
1. Whether I'm completely wrong and the solution of Oracle is perfect.
2. If Oracle is wrong, could you help me with this code?
Thank you in advance.
The question asks you to identify any employee whose salary and commission matches any combination of salary and salary of employees in Department 1700. The question doesn't say to exclude employees from that department. So it is quite correct for them to appear in the result set.
"One employee can't match themselves."
You're not matching employees, you're matching salaries . It just so happens that the list of target salaries is generated from a sub-query on department 1700: it could just as easily be a list of numbers from a CSV file.
Let's have a thought experiment. There are three employees in department 1700:
name | salary | comm
-----------+---------+-----
CHIP | 10,100 | 0
RUNCITER | 12,200 | 0
CONLEY | 10,500 | 0
Nobody else in the company earns above 10K.
So what is the answer to the question: how many employee have a salary and commission which match the salary and commission of any employee located in department 1700?
Three
Zero
Then Joe gets a modest raise and the salaries look like this:
name | salary | comm
-----------+---------+-----
CHIP | 10,500 | 0
RUNCITER | 12,200 | 0
CONLEY | 10,500 | 0
So what is the answer to the same question now?
Still three
Two
The answer is 'three' in both cases.
Why?
Because SQL is a set-based programming language, and the set of all employees who earn the exact salary and commission as employees in department 1700 must include all employees who work in department 1700.
There is a different set, which is the set of all employees who earn the exact salary and commission as employees in department 1700 who don't themselves work in department 1700. But that is not the set the SQL Fundamentals quiz is looking for.

Resources