Combine the result of one sql query into another - sql

I have the below table.
CREATE TABLE Employee_id_credits ( Employee_id, credits ) AS
SELECT 10, 1 FROM DUAL UNION ALL
SELECT 12, 1 FROM DUAL UNION ALL
SELECT 10, 1 FROM DUAL UNION ALL
SELECT 12, 1 FROM DUAL UNION ALL
SELECT 12, 1 FROM DUAL UNION ALL
SELECT 14, 1 FROM DUAL;
The below query groups and counts the total number of credits for employees.
select Employee_id, count(*) as "Total_credits"
from Employee_id_credits
group by Employee_id;
Gives the below output.
Employee_id Total_credits
----------- -------------
10 2
12 3
14 1
I have a Employee Manager table with the hierarchy.
CREATE TABLE Employee_Manager ( Employee_id, Manager_id ) AS
SELECT 10, 101 FROM DUAL UNION ALL
SELECT 12, 120 FROM DUAL UNION ALL
SELECT 13, 120 FROM DUAL UNION ALL
SELECT 14, 150 FROM DUAL UNION ALL
SELECT 101, NULL FROM DUAL UNION ALL
SELECT 120, 130 FROM DUAL UNION ALL
SELECT 130, NULL FROM DUAL;
I have a query to find the top level manager of the employee.
SELECT
Employee_id
FROM
Employee_Manager
WHERE
Manager_id is null
CONNECT BY PRIOR
Manager_id = Employee_id
START WITH
Employee_id = '12';
I want to combine the above two queries so that the output would look like below. How do I combine both queries?
Manager Total_credits
------- -------------
101 2
130 3
150 1

SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE Employee_id_credits ( Employee_id, credits ) AS
SELECT 10, 1 FROM DUAL UNION ALL
SELECT 12, 1 FROM DUAL UNION ALL
SELECT 10, 1 FROM DUAL UNION ALL
SELECT 12, 1 FROM DUAL UNION ALL
SELECT 12, 1 FROM DUAL UNION ALL
SELECT 14, 1 FROM DUAL;
CREATE TABLE Employee_Manager ( Employee_id, Manager_id ) AS
SELECT 10, 101 FROM DUAL UNION ALL
SELECT 12, 120 FROM DUAL UNION ALL
SELECT 13, 120 FROM DUAL UNION ALL
SELECT 14, 150 FROM DUAL UNION ALL
SELECT 101, NULL FROM DUAL UNION ALL
SELECT 120, 130 FROM DUAL UNION ALL
SELECT 130, NULL FROM DUAL;
Query 1 - Find the managers of each employee:
SELECT CONNECT_BY_ROOT( Employee_id ) AS Employee_id,
COALESCE( manager_id, employee_id ) AS manager_id
FROM Employee_manager
WHERE CONNECT_BY_ISLEAF = 1
CONNECT BY PRIOR Manager_id = Employee_id
Results:
| EMPLOYEE_ID | MANAGER_ID |
|-------------|------------|
| 10 | 101 |
| 12 | 130 |
| 13 | 130 |
| 14 | 150 |
| 101 | 101 |
| 120 | 130 |
| 130 | 130 |
Query 2 - Join that with the credits table and aggregate:
SELECT m.manager_id,
SUM( c.credits ) As total_credits
FROM Employee_id_credits c
INNER JOIN
(
SELECT CONNECT_BY_ROOT( Employee_id ) AS Employee_id,
COALESCE( manager_id, employee_id ) AS manager_id
FROM Employee_manager
WHERE CONNECT_BY_ISLEAF = 1
CONNECT BY PRIOR Manager_id = Employee_id
) m
ON ( c.employee_id = m.employee_id )
GROUP BY m.manager_id
Results:
| MANAGER_ID | TOTAL_CREDITS |
|------------|---------------|
| 101 | 2 |
| 130 | 3 |
| 150 | 1 |

Related

Find rows with the same values in a column

I've a table LESSON like this
| Student_id | Lesson_id |
| ---------- |- -------- |
| 352-03-3624| 10359427 |
| 352-03-3624| 10359449 |
| 805-17-4144| 58149917 |
| 805-17-4144| 58149968 |
I have to look for students who have taken the same lessons, ie who have the same lesson_id.
I used this query:
select * from lesson e
where exists
(select null from lesson i
where e.lesson_id = i.lesson_if and e.student_id <> i.student_id)
order by lesson_id
but it doesn't work very well.
Is there someone who can help me?
thanks
I'm finding for every studend if there is another student who follows the same lessons.
Just use the HAVING clause:
WITH lessons AS
(
SELECT '352-03-3624' as student_id, '10359427' as lesson_id FROM dual UNION ALL
SELECT '352-03-3624', '10359449' FROM dual UNION ALL
SELECT '805-17-4144', '58149917' FROM dual UNION ALL
SELECT '805-17-4144', '58149968' FROM dual UNION ALL
SELECT '805-17-4144', '10359427' FROM dual UNION ALL
SELECT '805-17-4143', '10359427' FROM dual UNION ALL
SELECT '805-17-4144', '10359449' FROM dual
)
SELECT lsns.lesson_id, lsns.student_id
FROM lessons lsns
, (SELECT COUNT(1), lesson_id
FROM lessons
GROUP BY lesson_id
HAVING COUNT(1) > 1) lsns_cnt
WHERE lsns_cnt.lesson_id = lsns.lesson_id;
PS. I added more data in order to have a result set. It contains the course and the student that are matching the criteria.
Another option might be to use count in its analytic form and then fetch rows whose count is larger than 1 (which means that there are two or more students who took the same lesson).
Sample data:
SQL> WITH lessons (student_id, lesson_id) AS
2 (
3 SELECT '352-03-3624', '10359427' FROM dual UNION ALL
4 SELECT '352-03-3624', '10359449' FROM dual UNION ALL
5 SELECT '805-17-4144', '58149917' FROM dual UNION ALL
6 SELECT '805-17-4144', '58149968' FROM dual UNION ALL
7 SELECT '805-17-4144', '10359427' FROM dual UNION ALL
8 SELECT '805-17-4143', '10359427' FROM dual UNION ALL
9 SELECT '805-17-4144', '10359449' FROM dual
10 )
Query begins here:
11 select lesson_id, student_id
12 from (select lesson_id,
13 student_id,
14 count(*) over (partition by lesson_id) cnt
15 from lessons
16 )
17 where cnt > 1
18 order by lesson_id, student_id;
LESSON_I STUDENT_ID
-------- -----------
10359427 352-03-3624
10359427 805-17-4143
10359427 805-17-4144
10359449 352-03-3624
10359449 805-17-4144
SQL>

Total salary from a hierarchical sql query in Oracle

I am trying to find the total salary by using oracle hierarchical SQL query but I do not get the desired output.
I use Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit.
Below are the sample input tables and the hierarchical structure.
Below is the desired output table.
Below is the code I wrote but its not summing up at all levels.
SELECT COALESCE(e.Manager_id, e.Employee_id) Employee_id,
(SELECT Employee_name
FROM Employee_table
WHERE Employee_id = COALESCE(Manager_id, Employee_id)) Employee_name,
SUM(s.Employee_salary)
FROM Employee_table e
JOIN Salary_table s
ON s.Employee_id = e.Employee_id
WHERE CONNECT_BY_ISLEAF = 1
CONNECT BY PRIOR s.Manager_id = s.Employee_id
GROUP BY COALESCE(e.Manager_id, e.Employee_id)
ORDER BY SUM(s.Employee_salary) DESC;
What am I doing wrong here?
Here is on approach using a standard recursive query rather than Oracle specific connect by syntax:
with cte (employee_id, employee_name, child_id) as (
select employee_id, employee_name, employee_id from employee where manager_id is null
union all
select c.employee_id, c.employee_name, e.employee_id
from employee e
inner join cte c on e.manager_id = c.child_id
)
select c.employee_id, c.employee_name, sum(s.employee_salary) total_salary
from cte c
inner join salary s on s.employee_id = c.child_id
group by c.employee_id, c.employee_name
order by c.employee_id
The recursive query starts from employees that have no manager, and retrieves the children record of each node. Then, the outer query brings the salary table, and aggregate by "root employee".
Demo on DB Fiddle:
EMPLOYEE_ID | EMPLOYEE_NAME | TOTAL_SALARY
----------: | :------------ | -----------:
1 | John | 4000
2 | Philip | 17900
9 | Joe | 5700
with Salary_table (Employee_id, Employee_salary) as (
select 1, 4000 from dual union all
select 2, 2500 from dual union all
select 4, 3400 from dual union all
select 5, 4500 from dual union all
select 6, 4300 from dual union all
select 7, 2000 from dual union all
select 8, 1200 from dual union all
select 9, 3100 from dual union all
select 11, 2600 from dual
)
, Employee_table (Employee_id, Employee_name, Manager_id) as (
select 1, 'John', null from dual union all
select 2, 'Phil', null from dual union all
select 3, 'Rayan', 2 from dual union all
select 4, 'Peter', 2 from dual union all
select 5, 'Mark', 2 from dual union all
select 6, 'Steve', 3 from dual union all
select 7, 'Margret', 3 from dual union all
select 8, 'Paul', 3 from dual union all
select 9, 'Joe', null from dual union all
select 10, 'Bose', 9 from dual union all
select 11, 'Jane', 9 from dual
)
select mgr_id, mgr_name, sum(employee_salary) from (
select employee_id, connect_by_root employee_id mgr_id,
connect_by_root employee_name mgr_name
from
employee_table e
start with manager_id is null
connect by prior employee_id = manager_id
)
join salary_table
using(employee_id)
group by mgr_id, mgr_name
order by 1;
MGR_ID MGR_NAM SUM(EMPLOYEE_SALARY)
---------- ------- --------------------
1 John 4000
2 Phil 17900
9 Joe 5700

Select Query with Recursion

I have two tables containing employee names (TableB) and Employee hierarchy( TableA) (manager_id from TableA can be employee_id in the same table ).
TableA
UniqueId Employee_ID Manager_ID
1 101 102
2 102 103
3 103 104
4 105 106
5 106 null
TableB
Employee_ID Employee_Name
101 First
102 Second
103 Third
104 Fourth
105 Fifth
106 Sixth
and I need output as below :
Employee_ID Employee_Name Transferred
101 First True
102 Second True
103 Third True
105 Fifth False
106 Sixth False
The Transferred column for each employee is calculated as =
isTransferred(Employee_ID)
{
If(Manager_ID is null) return false;
If(Manager_ID is present as employee_id in table A)
{
return isTransferred(manager_ID)
}
else
{
return true;
}
}
Is there any way to get the result in one select statement?
You can use a Recursive CTE and then get the last level of "recursion" for each employee. Once you have that, you just check the manager_id of that last level to find out if it's transferred.
For example:
with
tablea as (
select 1 as uniqueId, 101 as employee_id, 102 as manager_id from dual union all
select 2 as uniqueId, 102 as employee_id, 103 as manager_id from dual union all
select 3 as uniqueId, 103 as employee_id, 104 as manager_id from dual union all
select 4 as uniqueId, 105 as employee_id, 106 as manager_id from dual union all
select 5 as uniqueId ,106 as employee_id, null from dual
),
tableb as (
select 101 as employee_id, 'first' as employee_name from dual union all
select 102 as employee_id, 'second' as employee_name from dual union all
select 103 as employee_id, 'third' as employee_name from dual union all
select 104 as employee_id, 'fourth' as employee_name from dual union all
select 105 as employee_id, 'fifth' as employee_name from dual union all
select 106 as employee_id, 'sixth' as employee_name from dual
),
n (employee_id, employee_name, lvl, manager_id) as (
select b.employee_id, b.employee_name, 1, a.manager_id
from tablea a
join tableb b on a.employee_id = b.employee_id
union all
select
n.employee_id, n.employee_name, lvl + 1, a.manager_id
from n
join tablea a on a.employee_id = n.manager_id
),
m (employee_id, max_lvl) as (
select employee_id, max(lvl) from n group by employee_id
)
select n.employee_id, n.employee_name,
case when n.manager_id is not null then 'True' else 'False' end as transferred
from n
join m on n.employee_id = m.employee_id and n.lvl = m.max_lvl
order by n.employee_id
Result:
EMPLOYEE_ID EMPLOYEE_NAME TRANSFERRED
----------- ------------- -----------
101 first True
102 second True
103 third True
105 fifth False
106 sixth False
You can do it with a single pass through the tree as follows:
Outer join the management chain to the employees
Find the employees with no manager by mapping the unique_id to an "impossible" value, e.g. -1
Use the results of this join to walk down the tree, starting with the rows where the manager is null
Get the value for the expression in step 2 for the root rows; if this is the "impossible" value, then transfer => true
Filter out the rows for the impossible value
Which gives something like:
with table_a ( UniqueId, Employee_ID, Manager_ID ) as (
select 1, 101, 102 from dual union all
select 2, 102, 103 from dual union all
select 3, 103, 104 from dual union all
select 4, 105, 106 from dual union all
select 5, 106, null from dual
), Table_b ( Employee_ID, Employee_Name ) as (
select 101, 'First' from dual union all
select 102, 'Second' from dual union all
select 103, 'Third' from dual union all
select 104, 'Fourth' from dual union all
select 105, 'Fifth' from dual union all
select 106, 'Sixth' from dual
), rws as (
select b.*, a.Manager_ID,
nvl ( a.UniqueId, -1 ) transfer
from table_b b
left join table_a a
on b.Employee_ID = a.Employee_ID
)
select r.*,
case connect_by_root transfer
when -1 then 'true'
else 'false'
end transferred
from rws r
where transfer > 0
start with manager_id is null
connect by manager_id = prior employee_id;
EMPLOYEE_ID EMPLOYEE_NAME MANAGER_ID TRANSFER TRANSFERRED
103 Third 104 3 true
102 Second 103 2 true
101 First 102 1 true
106 Sixth <null> 5 false
105 Fifth 106 4 false

How to extract the number of subordinates from manager_id in the same table?

I've two columns: one with employee_id and one with manager_id
Apart from the President with employee_id 100 who doesn't have a manager (so manager_id is null) the rest have managers. For example, the President is the manager for two people with manager_id of 100. How to count and place it in this way:
employee_id 100 (column1) has 2 subordinates (column2)?
tried count, sum ,case, subquery and did't work
select employee_id,
manager_id,
first_name,
last_name,
case when employee_id = manager_id then count(employee_id) end,
count(manager_id)
from employees
--where manager_id is not null
group by manager_id,
employee_id,
first_name,
last_name
--having sum(manager_id) > 5
order by employee_id;
I expect to have 1st column as employee_id and second as the counted subordinates per employee_id.
Use a correlated hierarchical query:
Oracle Setup:
CREATE TABLE employees ( employee_id, manager_id ) AS
SELECT 100, NULL FROM DUAL UNION ALL
SELECT 101, 100 FROM DUAL UNION ALL
SELECT 102, 101 FROM DUAL UNION ALL
SELECT 103, 102 FROM DUAL UNION ALL
SELECT 104, 103 FROM DUAL UNION ALL
SELECT 105, 101 FROM DUAL UNION ALL
SELECT 106, 105 FROM DUAL UNION ALL
SELECT 107, 106 FROM DUAL UNION ALL
SELECT 108, 101 FROM DUAL UNION ALL
SELECT 109, 108 FROM DUAL;
Query:
SELECT employee_id,
(
SELECT COUNT(*)
FROM employees s
START WITH s.manager_id = e.employee_id
CONNECT BY PRIOR employee_id = manager_id
) AS num_subordinates
FROM employees e
Output:
EMPLOYEE_ID | NUM_SUBORDINATES
----------: | ---------------:
100 | 9
101 | 8
102 | 2
103 | 1
104 | 0
105 | 2
106 | 1
107 | 0
108 | 1
109 | 0
db<>fiddle here
if i understand your question, you could also do it with a simple Group by
this will count only the subordinates not the whole hierarchy
with tab as(
select 1 as emp_id, null as man_id from dual union all
select 2 as emp_id, 1 as man_id from dual union all
select 3 as emp_id, 1 as man_id from dual union all
select 2 as emp_id, null as man_id from dual union all
select 5 as emp_id, 2 as man_id from dual
)
select man_id as employee_id
, count(1) as cnt
from tab
where man_id is not null
group by man_id
EMPLOYEE_ID | CNT
----------: | --:
2 | 1
1 | 2
db<>fiddle here
Try this:
-- data preparation
WITH EMPS AS
(
SELECT 1001 AS EMP_ID, 'emp11' AS POS, 100 AS MGR_ID FROM DUAL UNION ALL
SELECT 1002 AS EMP_ID, 'emp12' AS POS, 100 AS MGR_ID FROM DUAL UNION ALL
SELECT 1003 AS EMP_ID, 'emp13' AS POS, 100 AS MGR_ID FROM DUAL UNION ALL
SELECT 2001 AS EMP_ID, 'emp21' AS POS, 200 AS MGR_ID FROM DUAL UNION ALL
SELECT 2002 AS EMP_ID, 'emp22' AS POS, 200 AS MGR_ID FROM DUAL UNION ALL
SELECT 100 AS EMP_ID, 'mgr1' AS POS, 1 AS MGR_ID FROM DUAL UNION ALL
SELECT 200 AS EMP_ID, 'mgr2' AS POS, 1 AS MGR_ID FROM DUAL UNION ALL
SELECT 1 AS EMP_ID, 'President' AS POS, NULL AS MGR_ID FROM DUAL )
-- Your actual query starts from here
SELECT
EE.EMP_ID,
EE.POS,
EE.MGR_ID,
CASE
WHEN EC.CNT IS NULL THEN 0
ELSE EC.CNT
END AS CNT
FROM
EMPS EE
LEFT JOIN (
SELECT
MGR_ID,
COUNT(1) AS CNT
FROM
EMPS
GROUP BY
MGR_ID
) EC ON EE.EMP_ID = EC.MGR_ID
ORDER BY
EE.EMP_ID;
Please add the other condition according to your needs.
DB Fiddle demo
Try this, use a select within the select
select emp.employee_id,
emp.manager_id,
emp.first_name,
emp.last_name,
(SELECT SUM(employees.employee_id) FROM employees where employees.manager_id=emp.employee_id) as subordinates,
count(manager_id)
from employees emp

Oracle SQL - Not in subquery

I'm newish to this and using Oracle SQL. I have the following tables:
Table 1 CaseDetail
CaseNumber | CaseType
1 | 'RelevantToThisQuestion'
2 | 'RelevantToThisQuestion'
3 | 'RelevantToThisQuestion'
4 | 'NotRelevantToThisQuestion'
Table 2 LinkedPeople
CaseNumber | RelationshipType | LinkedPerson
1 | 'Owner' | 123
1 | 'Agent' | 124
1 | 'Contact' | 125
2 | 'Owner' | 126
2 | 'Agent' | 127
2 | 'Contact' | 128
3 | 'Owner' | 129
3 | 'Agent' | 130
3 | 'Contact' | 131
Table 3 Location
LinkedPerson| Country
123 | 'AU'
124 | 'UK'
125 | 'UK'
126 | 'US'
127 | 'US'
128 | 'UK'
129 | 'UK'
130 | 'AU'
131 | 'UK'
I want to count CaseNumbers that are relevant to this question with no LinkedPeople in 'AU'. So the results from the above data would be 1
I've been trying to combine aggregate functions and subqueries but I think I might be over-complicating things.
Just need a push in the right direction, thanks!
To get all the records:
SELECT COUNT(DISTINCT CaseNumber)
FROM LinkedPeople
WHERE CaseNumber NOT IN
(
SELECT DISTINCT C.CaseNumber
FROM CaseDetail C
INNER JOIN LinkedPeople P ON C.CaseNumber = P.CaseNumber
INNER JOIN Location L
ON P.LinkedPerson = L.LinkedPerson
WHERE Country = 'AU' AND C.CaseType = 'RelevantToThisQuestion'
)
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE CASEDETAIL ( CaseNumber, CaseType ) AS
SELECT 1, 'RelevantToThisQuestion' FROM DUAL
UNION ALL SELECT 2, 'RelevantToThisQuestion' FROM DUAL
UNION ALL SELECT 3, 'RelevantToThisQuestion' FROM DUAL
UNION ALL SELECT 4, 'NotRelevantToThisQuestion' FROM DUAL;
CREATE TABLE LINKEDPEOPLE ( CaseNumber, RelationshipType, LinkedPerson ) AS
SELECT 1, 'Owner', 123 FROM DUAL
UNION ALL SELECT 1, 'Agent', 124 FROM DUAL
UNION ALL SELECT 1, 'Contact', 125 FROM DUAL
UNION ALL SELECT 2, 'Owner', 126 FROM DUAL
UNION ALL SELECT 2, 'Agent', 127 FROM DUAL
UNION ALL SELECT 2, 'Contact', 128 FROM DUAL
UNION ALL SELECT 3, 'Owner', 129 FROM DUAL
UNION ALL SELECT 3, 'Agent', 130 FROM DUAL
UNION ALL SELECT 3, 'Contact', 131 FROM DUAL;
CREATE TABLE LOCATION ( LinkedPerson, Country ) AS
SELECT 123, 'AU' FROM DUAL
UNION ALL SELECT 124, 'UK' FROM DUAL
UNION ALL SELECT 125, 'UK' FROM DUAL
UNION ALL SELECT 126, 'US' FROM DUAL
UNION ALL SELECT 127, 'US' FROM DUAL
UNION ALL SELECT 128, 'UK' FROM DUAL
UNION ALL SELECT 129, 'UK' FROM DUAL
UNION ALL SELECT 130, 'AU' FROM DUAL
UNION ALL SELECT 131, 'UK' FROM DUAL;
Query 1:
SELECT COUNT( DISTINCT CASENUMBER ) AS Num_Relevant_Cases
FROM CASEDETAIL c
WHERE CaseType = 'RelevantToThisQuestion'
AND NOT EXISTS ( SELECT 1
FROM LINKEDPEOPLE p
INNER JOIN LOCATION l
ON ( p.LinkedPerson = l.LinkedPerson )
WHERE c.CaseNumber = p.CaseNumber
AND l.Country = 'AU' )
Results:
| NUM_RELEVANT_CASES |
|--------------------|
| 1 |
I'm not sure about the exact syntax, but I believe you want something like:
select count distinct CaseNumber from LinkedPeople where Country != 'AU'