SQL subqueries multiple value - sql

I just need a little help about SQL queries
Here's the situation
ID FIRST_NAME LAST_NAME START_DAT END_DATE SALARY CITY DESCRIPTION
---- ---------- ---------- --------- --------- ---------- ---------- ---------------
01 Jason Martin 25-JUL-96 25-JUL-06 1234.56 Toronto Programmer
02 Alison Mathews 21-MAR-76 21-FEB-86 6661.78 Vancouver Tester
03 James Smith 12-DEC-78 15-MAR-90 6544.78 Vancouver Tester
04 Celia Rice 24-OCT-82 21-APR-99 2344.78 Vancouver Manager
05 Robert Black 15-JAN-84 08-AUG-98 2334.78 Vancouver Tester
06 Linda Green 30-JUL-87 04-JAN-96 4322.78 New York Tester
07 David Larry 31-DEC-90 12-FEB-98 7897.78 New York Manager
08 James Cat 17-SEP-96 15-APR-02 1232.78 Vancouver Tester
8 rows selected.
SQL> -- GROUP BY clause and AVG() function
SQL> SELECT city, AVG(salary)
2 FROM employee
3 GROUP BY city;
CITY AVG(SALARY)
---------- -----------
New York 6110.28
Toronto 1234.56
Vancouver 3823.78
The problem is I can't find a way to extract those names having higher salaries from each avg(salary) for city
Example:
Vancouver has avg(salary) of 3823.78 so I should get the name of 2 people: alison and james because they have higher salary than the avg(salary) of new york
For now I only go to this query but not working
select FIRST_NAME,SALARY,CITY
from employee
where SALARY > ( select avg(SALARY)
from employee
group by CITY
);
it tells me that subquery return more than 1 value
Hope someone can help me

Your query is returning more than one value. To restrict it to one value, you need a condition on city:
select FIRST_NAME,SALARY,CITY
from employee e
where SALARY > (select avg(SALARY)
from employee e2
where e2.city = e.city
group by CITY);
This is called a correlated subquery. You can also write it as a join, which is the syntax that I would use. The group by CITY is, strictly speaking, unnecessary, but I think it makes the intention of the subquery clearer.
You can rewrite this as a join by doing:
select e.FIRST_NAME, e.SALARY, e.CITY
from employee e join
(select city, avg(salary) as avgSalary
from employee
group by city
) ec
on e.city = ec.city
where e.salary > ec.avgSalary
Or, what would be my favorite way . . .
select e.FIRST_NAME, e.SALARY, e.CITY
from (select e.*,
avg(salary) over (partition by city) as avgSalary
employee e
) e
where e.salary > e.avgSalary

Related

find emp_names,max,min salary and number of employees on each department?

I use oracle 11g , so i have 2 tables(employees,departments):
desc employees: desc departments
EMPLOYEE_ID DEPARTMENT_ID
FIRST_NAME DEPARTMENT_NAME
LAST_NAME MANAGER_ID
EMAIL LOCATION_ID
PHONE_NUMBER
HIRE_DATE
JOB_ID
SALARY
COMMISSION_PCT
MANAGER_ID
DEPARTMENT_ID
and i want to get
employee_name,emp_names,emp_salary,dep_id,dep_names,max salary on each
dep , and min salary on each dep , and number of employees on each
department .
so i do this qouery:
select FIRST_NAME,DEPARTMENT_ID,max(SALARY),min(SALARY),count(EMPLOYEE_ID)
from employees join departments on employees.department_id = departments.departm
ent_id group by first_name,department_id;
but its give an error:
ERROR at line 1: ORA-00918: column ambiguously defined
however does my sql query right ?
I don't have your tables so I created views out of Scott's ones, to simulate what you have.
SQL> create or replace view employees as
2 select empno employee_id,
3 ename last_name,
4 deptno department_id,
5 sal salary
6 from emp;
View created.
SQL> create or replace view departments as
2 select deptno department_id,
3 dname department_name
4 from dept;
View created.
SQL>
Here's how I understood the question: list of employees per department should be separated from the rest (minimums, maximums, counts).
So: list of employees:
SQL> select d.department_name, e.last_name
2 from departments d join employees e on d.department_id = e.department_id
3 order by d.department_name;
DEPARTMENT_NAM LAST_NAME
-------------- ----------
ACCOUNTING CLARK
ACCOUNTING KING
ACCOUNTING MILLER
RESEARCH JONES
RESEARCH FORD
RESEARCH ADAMS
RESEARCH SMITH
RESEARCH SCOTT
SALES WARD
SALES TURNER
SALES ALLEN
SALES JAMES
SALES BLAKE
SALES MARTIN
14 rows selected.
Aggregates: outer join for departments that don't have any employees:
SQL> select d.department_name,
2 min(e.salary) min_sal,
3 max(e.salary) max_sal,
4 count(e.employee_id) cnt_emp
5 from departments d left join employees e on d.department_id = e.department_id
6 group by d.department_name
7 order by d.department_name;
DEPARTMENT_NAM MIN_SAL MAX_SAL CNT_EMP
-------------- ---------- ---------- ----------
ACCOUNTING 1300 5000 3
OPERATIONS 0
RESEARCH 800 3000 5
SALES 950 2850 6
LISTAGG allows you to list all employees per department in the same statement, though; see line 5. I, somehow, doubt that you learnt about that function yet (as you struggle with such a problem).
SQL> select d.department_name,
2 min(e.salary) min_sal,
3 max(e.salary) max_sal,
4 count(e.employee_id) cnt_emp,
5 listagg(e.last_name, ', ') within group (order by e.last_name) employees
6 from departments d left join employees e on d.department_id = e.department_id
7 group by d.department_name
8 order by d.department_name;
DEPARTMENT_NAM MIN_SAL MAX_SAL CNT_EMP EMPLOYEES
-------------- ---------- ---------- ---------- -------------------------------------------
ACCOUNTING 1300 5000 3 CLARK, KING, MILLER
OPERATIONS 0
RESEARCH 800 3000 5 ADAMS, FORD, JONES, SCOTT, SMITH
SALES 950 2850 6 ALLEN, BLAKE, JAMES, MARTIN, TURNER, WARD
SQL>

SQL - counting max number of employees and excluding string using subqueries

These are my two tables:
employee
employee_id employee_name job manager_id hire_date salary commission department_id
----------------------------------------------------------------------- -------------------------------------
7839 KING PRESIDENT 20-NOV- 01 5000 50
7596 JOST VICE PRESIDENT 7839 04-MAY- 01 4500 50
7603 CLARK VICE PRESIDENT 7839 12-JUN- 01 4000 50
7566 JONES PUBLIC ACCOUNTANT 7596 05-APR- 01 3000 10
7886 STEEL PUBLIC ACCOUNTANT 7566 08-MAR- 03 2500 10
7610 WILSON ANALYST 7596 03-DEC- 01 3000 20
7999 WOLFE ANALYST 7610 15-FEB- 02 2500 20
7944 LEE ANALYST 7610 04-SEP- 06 2400 20
7900 FISHER SALESMAN 7603 06-DEC- 01 3000 500 30
7921 JACKSON SALESMAN 7900 25-FEB- 05 2500 400 30
7952 LANCASTER SALESMAN 7900 06-DEC- 06 2000 150 30
7910 SMITH DATABASE ADMINISTRATOR 7596 20-DEC- 01 2900 40
7788 SCOTT PROGRAMMER 7910 15-JAN- 03 2500 40
7876 ADAMS PROGRAMMER 7910 15-JAN- 03 2000 40
7934 MILLER PROGRAMMER 7876 25-JAN- 02 1000 40
8000 BREWSTER TBA 22-AUG- 13 2500
department
department_id department_name address
------------- -------------------- --------------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 IT DALLAS
50 EXECUTIVE NEW YORK
60 MARKETING CHICAGO
I need to write a statement using subqueries (not joins) to display the name and address of all departments (excluding departments in Dallas) having the maximum number of employees. I cannot have any hard coding except the string 'DALLAS'
The result should look like this:
DEPARTMENT_NAME ADDRESS
-------------------- --------------------
EXECUTIVE NEW YORK
SALES CHICAGO
I'm at a loss on doing this without joins. Here are my three tries:
SELECT department_name, address
FROM department
WHERE department_id NOT IN
(SELECT department_id
FROM department
WHERE UPPER(address) + 'DALLAS')
ORDER BY department_name;
SELECT department_name, address
FROM department
WHERE department_id NOT IN
(SELECT department_id
FROM department
WHERE UPPER(address) = 'DALLAS')
AND department_id > ALL
(select COUNT(department_id) FROM employee GROUP BY department_id)
ORDER BY department_id;
select *
from department
where department_id in (select department_id
from employee
group by department_id
having count(*) = (select max(num)
from (select department_id,
count(*) as num
from employee_tbl
where address != 'DALLAS'
group by department_id)));
Can anyone PLEASE tell me if I'm even getting close and guide me in the right direction? Thanks
The departments with the maximum number of employees (using a single table scan):
SELECT department_id
FROM (
SELECT department_id,
RANK() OVER ( ORDER BY num_employees DESC ) AS rnk
FROM (
SELECT department_id,
COUNT(1) AS num_employees
FROM employees
GROUP BY department_id
)
)
WHERE rnk = 1;
Finding the departments not in DALLAS:
SELECT department_id
FROM departments
WHERE address <> 'DALLAS'
So combining the two:
SELECT department_id
FROM (
SELECT department_id,
RANK() OVER ( ORDER BY num_employees DESC ) AS rnk
FROM (
SELECT department_id,
COUNT(1) AS num_employees
FROM employees
WHERE department_id IN (
SELECT department_id
FROM departments
WHERE address <> 'DALLAS'
)
GROUP BY department_id
)
)
WHERE rnk = 1;
maybe something like this?
select department_name, address
from department
where department_id in
(SELECT e.department_id
FROM employee e
group by e.department_id
having count(*) = (
select max(count(e2.employee_id))
FROM employee e2
WHERE department_id NOT IN (SELECT department_id
FROM department
WHERE UPPER(address) = 'DALLAS')
group by e2.department_id
)
)
and UPPER(address) != 'DALLAS'
but it is not the best query in plane :) very strange
In your query Ellen I did not understand where from the table
employee_tbl

SQL - select name and address of all departments having max number of employees

I have these tables:
department
DEPARTMENT_ID DEPARTMENT_NAME ADDRESS
------------- -------------------- --------------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 IT DALLAS
50 EXECUTIVE NEW YORK
60 MARKETING CHICAGO
employee
Employee_ID employee_name job manager_ID hire_date salary commission department_ID
------------------------------------------------------------------------------------------------------------
7839 KING PRESIDENT 20-NOV-01 5000 50
7596 JOST VICE PRESIDENT 7839 04-MAY-01 4500 50
7603 CLARK VICE PRESIDENT 7839 12-JUN-01 4000 50
7566 JONES PUBLIC ACCOUNTANT 7596 05-APR-01 3000 10
7886 STEEL PUBLIC ACCOUNTANT 7566 08-MAR-03 2500 10
7610 WILSON ANALYST 7596 03-DEC-01 3000 20
7999 WOLFE ANALYST 7610 15-FEB-02 2500 20
7944 LEE ANALYST 7610 04-SEP-06 2400 20
7900 FISHER SALESMAN 7603 06-DEC-01 3000 500 30
7921 JACKSON SALESMAN 7900 25-FEB-05 2500 400 30
7952 LANCASTER SALESMAN 7900 06-DEC-06 2000 150 30
7910 SMITH DATABASE ADMINISTRATOR 7596 20-DEC-01 2900 40
7788 SCOTT PROGRAMMER 7910 15-JAN-03 2500 40
7876 ADAMS PROGRAMMER 7910 15-JAN-03 2000 40
7934 MILLER PROGRAMMER 7876 25-JAN-02 1000 40
8000 BREWSTER TBA 22-AUG-13 2500
I need to display the name and address with the exception of Dallas, having the maximum number of employees.
I have written this:
SELECT department_name, address
FROM department
WHERE department_id IN
(SELECT MAX(department_id)
FROM department
WHERE UPPER(address) != 'DALLAS')
ORDER BY department_name;
But I'm only getting one line
DEPARTMENT_NAME ADDRESS
-------------------- --------------------
MARKETING CHICAGO
What am I doing wrong?
Not sure I understood your question, but I think you want this:
with emp_count as (
select d.department_name,
d.address,
count(*) as num_emps,
max(count(*)) over () as max_count
from department d
join employee e on d.department_id = e.department_id
where address <> 'DALLAS'
group by d.department_name, d.address
)
select *
from emp_count
where num_emps = max_count;
This returns:
DEPARTMENT_NAME | ADDRESS | NUM_EMPS
----------------+----------+---------
SALES | CHICAGO | 3
EXECUTIVE | NEW YORK | 3
SQLFiddle example: http://sqlfiddle.com/#!4/05db83/1
Based on your sample data the city seems to be recorded in the address column of the department table, and that more than one department can have the same city.
Given this, and the fact that you want to "exclude Dallas," as you say, I would assume you want to filter out all departments where the associated address column is 'Dallas'.
However, I don't know if...
you want to exclude departments at Dallas if they happen to be tied with other department(s) for the most employees, or
if you want to exclude departments at Dallas from being considered when determining what the highest number of employees is at any department.
If (1) is true:
select *
from department_tbl
where department_id in (select department_id
from employee_tbl
group by department_id
having count(*) = (select max(num_emps)
from (select department_id,
count(*) as num_emps
from employee_tbl
group by department_id)))
and address <> 'DALLAS';
If (2) is true:
select *
from department_tbl
where department_id in (select department_id
from employee_tbl
group by department_id
having count(*) = (select max(num_emps)
from (select department_id,
count(*) as num_emps
from employee_tbl
where address <> 'DALLAS'
group by department_id)));
Try this.
SELECT d.department_name,
d.address
FROM department d
JOIN employee e
ON ( d.department_id = e.department_id )
WHERE d.address <> 'DALLAS'
GROUP BY d.department_name,
d.address
HAVING Count(*) = (SELECT Max(cnt)
FROM (SELECT Count(*) CNT
FROM employee e
WHERE NOT EXISTS (SELECT 'x'
FROM department d
WHERE d.department_id =
e.department_id
AND d.address = 'DALLAS')
GROUP BY department_id))
select e.department_id, d.department_name, count(e.department_id)
from employee e, department d
where e.department_id = d.department_id
group by e.department_id, d.department_name
having count(e.department_id)=(select max(count(department_id))
from employee
group by department_id);
Hope it helps

Department names(with employee name) with more than 2 employee and salary greater than 90% of respective department average salary

I am trying to generate a SQL query to find the Department names(with employee name) with more than 2 employee whose salary greater than 90% of respective department average salary. My SQL Code is working fine , it has no Syntax errors but the output is giving me additional data. The table is as follos
JONES ACCOUNTING 3000
STEEL ACCOUNTING 2500
WILSON RESEARCH 3000
WOLFE RESEARCH 2500
LEE RESEARCH 2400
LANCASTER SALES 2000
JACKSON SALES 2500
FISHER SALES 3000
ADAMS IT 2000
MILLER IT 1000
SCOTT IT 2500
SMITH IT 2900
KING EXECUTIVE 5000
JOST EXECUTIVE 4500
CLARK EXECUTIVE 4000
My code is as follows.
Select department_name , employee_name
from department d , employee e
where e.department_id = d.department_id
and (SELECT COUNT(*)
FROM Employee E
WHERE E.department_ID = D.department_ID) > 2
and salary >
0.9*(SELECT ROUND(AVG(salary),2)
FROM employee e_inner
WHERE e_inner.department_id = e.department_id);
I notice that my code returns the value of department with more than 2 employees and salary > 90% of department's average salary. whereas I am looking for departments with more than 2 employees whose salary is more than 90% of department avg salary
I think this should do it:
select *
from (
select department_name,
employee_name,
sum(case when salary > avg_dept_sal * 0.9 then 1 else 0 end) over (partition by department_id) as greater_count
from (
select d.department_name,
e.department_id,
e.employee_name,
e.salary,
count(*) over (partition by e.department_id) as dept_count,
avg(salary) over (partition by e.department_id) as avg_dept_sal
from employee e
join department d on e.department_id = d.department_id
) t1
) t2
where greater_count >= 2
This will return all employees of those departments. If you only want to see those employees whose salary is actually greater than the 90% you need to add another condition to the outer where clause to only select those.
The below stated query will give result as desired without using any analytics. Simple & Easy just like English.
**
*
with T as (select empname, deptno, salary from employee e1 where
salary > (select avg(salary)*0.9 from employee e2 where
e2.deptno=e1.deptno group by deptno having count(empname)>2)) select
T.empname, deptname, salary from T, department where
T.deptno=department.deptno;
*
**
I executed this query successfully on Oracle database.

query to display the highest salaries of employees by age

Can you help me? Need to get a list of the highest salaries of employee with the sample by age,first name and last name
Input:
Age FirstName LastName SAL
---------- ---------- ---------- ----------
30 Andy Donald 175
31 Petr Pess 295
30 John Jacky 453
31 Bob Bobby 385
29 Eric Rice 957
Answer should be
Age FirstName LastName SAL
---------- ---------- ---------- ----------
31 Bob Bobby 385
30 John Jacky 453
29 Eric Rice 957
Thanks in advance
If you have window/analytic functions available (you don't mention an RDBMS in the OP), you can do the following:
SELECT * FROM (
SELECT Age, FirstName, LastName, SAL
, DENSE_RANK() OVER (PARTITION BY Age ORDER BY SAL DESC) AS ranknum
FROM employees
) WHERE ranknum = 1
This will work even when two or more employees of the same age have the same salary - both will be returned. It will also allow you to get the 2nd highest salary, etc., if you want (just change ranknum = 1 to ranknum = 2, etc.).
Edit: FYI, this will work in Oracle, SQL Server, and PostgreSQL at least.
Get all employees for which there is no employee with the same age and with a higher salary:
SELECT *
FROM employees e1
WHERE NOT EXISTS (
SELECT 1
FROM employees e2
WHERE e1.age = e2.age
AND e1.sal < e2.sal
)
ORDER BY age DESC
If two employees have the same age and salary, both will be returned... This query will work on any database
If you are using MySQL, the following should work:
select * from
(select * from myTable order by age desc, sal desc) sq
group by age
(Although it won't return multiple rows for employees of the same age on the same salary.)