Find span of control from an employee table using SQL - sql

I have the following table:
EMPNO
ENAME
JOB
MGR
HIREDATE
SAL
COMM
DEPTNO
7698
BLAKE
MANAGER
7839
05/01/1981
2850
-
30
7839
KING
PRESIDENT
-
11/17/1981
5000
-
10
7782
CLARK
MANAGER
7839
06/09/1981
2450
-
10
What is a query that will return span of control, or the number of people reporting to each manager, given that the same 4-digit numbers appear in both the EMPNO, as well as the MGR columns?
If possible, I'd like to know how to get the result using both a subquery and/or a self join.
I used the following query:
SELECT e.ename, m.ename
from emp e, emp m
WHERE e.empno = m.mgr
It results in a table that lists the names of each employee that correspond to each manager. Now, I just need to count the results and return a single number of employees grouped by each manager. I've tired to nest the above query in a general query using the COUNT function, but can't get it to work. A sample output of what I'd like to see is as follows:
(manager)ENAME
(direct reports)COUNT_OF_ENAME
BLAKE
n
KING
n
CLARK
n

The following query will give you the manager id and a count of how many report to them
SELECT MGR as MID, COUNT(*) AS REPORT_COUNT
FROM emp
GROUP BY MGR
Now we need to get the manager name with a join
SELECT E.ENAME, REPORT_COUNT
FROM (
SELECT MGR as MID, COUNT(*) AS REPORT_COUNT
FROM emp
GROUP BY MGR
) X
JOIN emp E ON E.empno = x.MID

Related

SELECT STATEMENT WITH ORDER BY

I have a simple select statement with joins in oracle I have one agregate function like sum(COLUMN) , my query works fine and it return the result , Now I am adding one-two more columns from same table to which I have a join , I am requiring simple column values no agregate function when I add first column it ask me that it needed to be in group by as it is not agregate function I did that , but after doing that i am getting more number of rows which i were getting previously what could be the reason, when I add one more columns than again number of rows increased like with my first query without adding these two columns the result was 544 and now it is 766
select distinct 'carTypes' as type, mi.WOMENNAME as "mother name", mi.womencnic as "mother CNIC", '' as BFORMNO, '' as child_gender, mi.PROVINCE, mi.district, mi.tehsil, mi.HUSBANDNAME, mi.PHONENO contact, mi.address, SUM(p.amount) paid_amount
from MINFORMATION mi
inner join PAYMENTINFORMATION p on p.BENEFICIARYID=mi.WOMENID and p.BANKSTATUS='Successfull'
where mi.month IS NOT NULL
and p.generationdate between '24-JUN-21' and '01-JAN-22'
group by mi.WOMENNAME, mi.womencnic,mi.PROVINCE, mi.district, mi.tehsil, mi.HUSBANDNAME, mi.PHONENO , mi.address
above is my query
That's how it goes. When there's an aggregate function in the select column list all non-aggregated columns must be included in the group by clause (group by, not order by as your title suggests; order by is irrelevant in this matter).
For example, as of Scott's emp table: if you want to compute sum of salaries, the result is only one row:
SQL> select sum(sal)
2 from emp;
SUM(SAL)
----------
29025
If you add deptno column, it means you want to compute sum of salaries per each department. As there are employees from 3 departments, it results in 3 rows:
SQL> select deptno, sum(sal)
2 from emp
3 group by deptno;
DEPTNO SUM(SAL)
---------- ----------
30 9400
20 10875
10 8750
Adding yet another column - job - means that you want to calculate sum of salaries per each department, and per each job within that department; all that increases number of resulting rows:
SQL> select deptno, job, sum(sal)
2 from emp
3 group by deptno, job
4 order by deptno, job;
DEPTNO JOB SUM(SAL)
---------- --------- ----------
10 CLERK 1300
10 MANAGER 2450
10 PRESIDENT 5000
20 ANALYST 6000
20 CLERK 1900
20 MANAGER 2975
30 CLERK 950
30 MANAGER 2850
30 SALESMAN 5600
9 rows selected.
Therefore, what kind of output would you want to get? If you could explain it, maybe we could suggest approach that would make it happen.

SQL Server 2014 Management Studio update query

I have a table Employee with those columns
empid, empname, job, hiredate, sal, comm, deptno
I want to verify if this is correct:
Number of employees in each department
How many people there are in each type of job in each department
Display the department and number of employees in department with fewer than 6 employees
Find the employee name and its salary who is earning maximum salary in dept 20
Here is what I have tried:
Query #1:
select DEPTNO, count(*) AS NO_OF_PERSONS
from EMP
group by DEPTNO;
Query #2:
select job, count(*) AS NO_OF_PERSONS
from EMP
group by job;
Query #3:
update EMPLOYEE
set sal = sal + 1000
where com > 2500;
And I am unable to do the 4th part.
I hope that below queries will help you.
no of employees in each dept?
SELECT DEPTNO,
count(*) AS NO_OF_PERSONS
FROM EMP
GROUP BY DEPTNO;
how many people are there in each type of job in each department?
SELECT job,
deptno,
count(*) AS NO_OF_PERSONS
FROM EMP
GROUP BY job,
deptno;
display the department and no of employees in department with fewer than 6 employee.
SELECT deptno,
count(*)
FROM emp
GROUP BY deptno
HAVING count(*) < 6;
find the employee name and its salary who is earning maximum salary in dept 20.
SELECT Max(salary_amount),
empid
FROM EMP
WHERE deptno = 20
GROUP BY empid;
You can start learning Basic SQL here
select e1.name, e1.sal
from EMP e1
where e1.DEPTNO = 20
and not exists(select *
from EMP e2
where e2.DEPTNO = 20
and e2.sal > e1.sal);

SQL select rows without duplicates

Practicing some SQL, we have to get the name of the employees whose salary is the greatest of his department. But if in any department there were more than one employer with the greatest salary, we would not have to consider that department.
We got the first part but not the second one (because there are two employees with the same greatest salary (3,000) in the same department (20)).
This is what we did:
SQL> SELECT ename, sal, deptno FROM emp a
WHERE sal >= ALL (SELECT sal FROM emp WHERE deptno=a.deptno)
ORDER BY sal;
And this is what we got:
ENAME SAL DEPTNO
---------- ------- ------
BLAKE 2,850 30
FORD 3,000 20
SCOTT 3,000 20
KING 5,000 10
4 filas seleccionadas.
Any help will be useful, thank you!
SELECT ename, sal, deptno
FROM emp a
WHERE not exists (
SELECT *
FROM emp
WHERE deptno=a.deptno
and sal >= a.sal
and ename != a.ename)
ORDER BY sal;
with cte as
( SELECT ename, sal, deptno
, row_number() over (partition by deptno order by sal desc) as rn
FROM emp
)
select ename, sal, deptno from cte where rn = 1
except
select ename, sal, deptno from cte where rn = 2
order by sal
if this does not work in oracle - it used to be also tagged mssql
You can have what you need with some analytic functions:
select ename,
deptno,
sal
from (
select ename,
deptno,
sal,
row_number() over(partition by deptno order by sal desc) AS num,
count(1) over(partition by deptno, sal) AS count
from emp
)
where num = 1
and count = 1
The inner query orders by salary and counts the number of employees with the same salary in the same department; the outer one simply filters for employees with the maximum salary, where only one employee has that salary in the department.
With a different approach, simply modifying your query, you can try:
SELECT ename, sal, deptno FROM emp a
WHERE sal >= ALL (SELECT sal FROM emp WHERE deptno=a.deptno)
and (select count(1) from emp b where a.deptno = b.deptno and a.sal = b.sal) = 1
The first way gives better performance, with a single table scan, while the second one needs a nested query, thus being less efficient
try to use GROUP BY column_name`, it will show the record without duplicate.

LISTAGG in ORACLE

I am trying to use LISTAGG() to fetch more than two columns.
SELECT deptname, deptno, LISTAGG(ename, ',') WITHIN GROUP (ORDER BY ename) AS employees
FROM emp
GROUP BY deptno;
But it is throwing this error:
: FROM keyword not found where expected
00000 - "FROM keyword not found where expected"
*Cause:
*Action:
Error at Line: 3 Column: 12
Can please somebody explain why it is?
The LISTAGG analytic function was introduced in Oracle 11g Release 2. So, if you are on older version, you won't be able to use it.
The error seems strange. You should actually get ORA-00904: "DEPTNAME": invalid identifier as the standard EMP table in SCOTT schema doesn't have DEPTNAME column. Also, you should get ORA-00979: not a GROUP BY expression as you did not mention the SELECTed columns in the GROUP BY expression.
Using the standard EMP table in SCOTT schema:
SQL> SELECT deptno,
2 job,
3 LISTAGG(ename, ',') WITHIN GROUP (
4 ORDER BY ename) AS employees
5 FROM emp
6 GROUP BY deptno,
7 job;
DEPTNO JOB EMPLOYEES
---------- --------- ------------------------
10 CLERK MILLER
10 MANAGER CLARK
10 PRESIDENT KING
20 CLERK ADAMS,SMITH
20 ANALYST FORD,SCOTT
20 MANAGER JONES
30 CLERK JAMES
30 MANAGER BLAKE
30 SALESMAN ALLEN,MARTIN,TURNER,WARD
9 rows selected.
SQL>
Try:
SELECT deptname, deptno, LISTAGG(ename, ',') WITHIN GROUP (ORDER BY ename) AS employees
FROM emp
GROUP BY deptno,deptname;
Oracle 11g:
SELECT deptname, deptno, LISTAGG(ename, ',') WITHIN GROUP (ORDER BY deptno,job) AS employees
FROM emp
GROUP BY deptno,job;
Oracle 10g:
SELECT deptname, deptno, WMSYS.WM_CONCAT(ename) AS employees
FROM emp
GROUP BY deptno,job;

Oracle SQL - Update a database field with the content of the next record

I am trying to anonymize the 'name' field of a customer table. I want to replace the 'name' of every record with the name from the customer in the next record. (I know: That's not really anonymous but 'name' and 'customerId' won't match after that. That's enough for my purposes)
I tried this, but I get an ORA-01747 error.
UPDATE Customer A
SET NAME =
(SELECT NAME
FROM Customer
WHERE ROWNUM = A.ROWNUM + 1)
What is wrong? How can I update every 'name'-field with the content of the next 'name'-field in the table?
ROWNUM is a pseudocolumn, it's not stored with the data, its part of your result set. Additionally, typically relational databases have no concept of row order.
We can probably work out a kludgy way to do it, but instead, couldn't you just instead do something like:
UPDATE CUSTOMER SET NAME = DBMS_RANDOM.STRING('a', 10);
In Oracle, this updates every customer with a random string of 10 alphanumeric digits.
Mix'em all!!!
merge into Customer dest
using (
select r, name from
(select name, row_number() over (order by dbms_random.value) n from Customer)
join (select rowid r, rownum n from Customer) using(n)
) src
on (dest.rowid = src.r)
when matched then update set
dest.name = src.name;
You need to use LEAD().
Corrected as per 'a_horse_with_no_name' comments: LEAD(sal, 1, sal)
UPDATE emp_test a
SET sal =
(
SELECT LEAD(sal, 1, sal) OVER (ORDER BY sal) AS sal_next
FROM scott.emp b
WHERE a.empno = b.empno
)
/
Same with ename:
SELECT empno, ename, job, sal,
LEAD(ename, 1, ename) OVER (ORDER BY ename) AS name_next
FROM scott.emp
/
EMPNO ENAME JOB SAL NAME_NEXT
--------------------------------------------
7876 ADAMS CLERK 1100 ALLEN
7499 ALLEN SALESMAN 1600 BLAKE
7698 BLAKE MANAGER 2850 CLARK
7782 CLARK MANAGER 2450 FORD
....
7844 TURNER SALESMAN 1500 WARD
7521 WARD SALESMAN 1250 WARD
This will not work:
SELECT * FROM scott.emp
WHERE ROWNUM = 5
/
But this will:
SELECT * FROM scott.emp
WHERE ROWNUM <= 5
/
This might work but is untested.
UPDATE Customer A
SET NAME =
(SELECT NAME
FROM Customer
WHERE ROWNUM = (SELECT (A.ROWNUM + 1))