Hierarchical query with join. Performance issue - sql

I am trying to form a query that fetch the details of subordinates under multiple level of supervisors. The hierarchy is as follows:
General manager- regional manager- district manager - supervisor - employee
My requirement is that, any manager in this hierarchy should be able to see the details of the employee at the bottom of the hierarchy.
So far I have tried the following options:
1: 102 seconds to execute
WITH w AS
( select personId from assignments start with supervisorId = :inputId
Connect by prior personId = supervisorId)
Select columns FROM a,b
where a.column = b.column
And a.personId = w.personId;
2: 92 seconds
Select columns FROM a,b
where a.column = b.column
And a.personId IN ( select personId from assignments start with supervisorId = :inputId
Connect by prior personId = supervisorId);
3:0.75 seconds[no data fetched]
WITH w AS ( select columns FROM a,b
where a.column = b.column)
Select w.columns from w, assignments
Where t.personId = assignments.personId
Start with supervisorId = :inputId
Connect by prior personId = supervisorId.
In option 3, since join condition is executed first before connect by, that could be why it does not return any rows. Please note that when fetching data for a single employee, it takes less than a second to execute, ie by removing the hierarchical query part and the assignments table and giving personId of a single person.
So I am looking for an efficient solution to meet this requirement. Any method, even not including hierarchical queries are welcome.
Thanks,
Anoop

Related

How to tune query to fetch result faster | Oracle 19c |

I have a table which as huge records in table
My tables : employee and customer
Now the issue here is I have 2 billion records in employee table and 1 billion records in customer table
Employee columns
empid
empname
empage
empdcourse
Customer columns
custid
custdesc
custmessage
My query :
select emp_id from employee where empid not in ( select custid from customer);
Error : It throws me table space issue. Not allowed to increase table space
Is their any way I can tune my query or run in batch by batch so I get output
Any solution is much appreciated !!!
Need it on high priority
NOT EXISTS may be more efficient and less memory consuming in such case.
(The query suggests Customer and Employee share the same PK, does it mean you have an "super" table Person ?)
Try this:
with tmp as
(select /*+full(c)*/
custid
from customer c)
select /*+full(e)*/
e.emp_id
from employee e, tmp t
where e.empid = t.custid(+)
and t.custid is null;
The hint full will prevent the tablespace issue.
The OUTER JOIN is faster than the NOT IN.
You can improve it by adding the hint parallel, starting with a degree=2 or 4 like this:
with tmp as
(select /*+full(c) parallel(c,2)*/
custid
from customer c)
select /*+full(e) parallel(e,2)*/
e.emp_id
from employee e, tmp t
where e.empid = t.custid(+)
and t.custid is null;
You can add indexes for columns, for example, if they aren’t primary keys:
CREATE INDEX empid_index
ON employee(empid);
Also, you can update the query:
select e.empid from employee e where not exists (select 1 from customer c where c.custid = e.empid);

Level1, level2, level3.. nth level display of managers of an employee sql

Can someone please let me know how this can be accomplished?
I have a table with following fields and data- employeeid, email are unique
what my output should look like for Donnie-
I have to append the hierarchy to each employee row. Suppose employee- don's manager bob has katie as direct manager instead of john then it should get displayed as level1->john, level2->katie,level3->bob, ename->don, direct manager-bob. This could go up to 5 levels. If one of the managers left the job then the employee should get assigned to the prior level manager.
Thanks for your help in advance
First of all, you need to fix your table design so it uses managerid, not managername; at the moment your model doesn't allow for two managers with the same first name.
Secondly, please tag your question with the DBMS you are using.
If you are sure there are only going to be a maximum of 5 levels then just left outer join the table to itself 5 times - each time using the managerid of the current instance of the join joined to the employeeid of the next instance of the join:
SELECT ...
FROM table a
left outer join table b on a.managerid = b.employeeid
left outer join table c on b.managerid = c.employeeid
...
If the hierarchy might change then you ought to be looking at hierarchical queries - but the syntax for these are often DBMS-specific, hence the request for you to tag your question with the DBMS you are using (though you should do this anyway as it's good practice)
with s (eid, ename, mname, lvl) as
(select eid, ename, mname, 1 as lvl
from tbl
union all
select eid, ename, mname, a.lvl+1
from s a,
tbl b
where a.mname=b.ename)
select eid,
max(ename),
max(mname),
max(case when lvl=1 then mname end) lv11name,
max(case when lvl=2 then mname end) lv12name,
max(case when lvl=3 then mname end) lv13name
from s ;
This query is working for me. I still have to accommodate the scenario where if manager left the job, I need to update the employee hierarchy to the prior level supervisor. Please let me know how that can be handled in the above sql

"Column "parent_id" for "cte" is specified more than once" in SQL recursive query

I have 5 SQL tables with columns as follows:
tbl_request_listEmpB
listEmpB_id request_id
tbl_request_listEmpD
listEmpD_id request_id
tbl_employee
id, parent_id (this one refers to id in tbl_department)
tbl_department
id, parent_id (that one referes to id of parent department)
tbl_department_manager
department_id, manager_employee_id
As input data I have employee_id and request_id.
I need to figure out whether the employee has access to the request (whether he's a manager or not)
Here's the query which is supposed to return 1 if the current user is a manager, 0 otherwise
with reqEmployees as (
select listEmpB_id as employee_id
from tbl_request_listEmpB
where request_id = ${request_id}
union all --concatenate the two tables
select listEmpD_id
from tbl_request_listEmpD
where request_id = ${request_id}
),
cte as (
select e.parent_id, null as parent_id
from reqEmployees r
join tbl_employee e on e.id = r.employee_id -- get these employees' departments
union all
select d.id, d.parent_id
from cte
join tbl_department d on d.id = cte.parent_id -- and get parent departments
)
select case when exists (select 1
from cte
join tbl_department_manager dm on dm.department_id = cte.id
where dm.manager_employee_id = ${employee_id})
then 1 else 0 end;
Finally, there's the logic that I believe is implemented in the query above:
First we need to identify whether the employee_id is a manager or not. If he is - find in which departments. So we query to tbl_department_manager based on manager_employee_id(=employee_id from input data) to get a list of corresponding department_id and store them in a variable. If the query returned 0 departments - terminate and return false
Based on request_id we collect ids of employees from both tbl_request_listEmpB and tbl_request_listEmpD. Later we refer to them as employee_id from reqEmployees
Query to tbl_employee based on ids retrieved from p.2 to get parent_id (list of unique departments employees belong to)
If there's a match between at least one department from p.1 and a one from p.3 return true
If not, there's a need to query to tbl_department and recursively search for a match between at least one element from p.1 and one element in p.3.parent_id
Here's what I mean
Consider the following chart
And here's the corresponding SQL table:
tbl_department (id, parent_id)
dep0 null
dep1 dep0
dep2 dep1
dep3 dep1
dep4 dep2
dep5 dep0
So, if we have a departments list returned from p.1 of ['dep1'] (there might be more than one element, we have to iterate through each element) we need to return true ONLY if from p.3 we've got dep1|dep2|dep3|dep4 - (dep1 descendants including dep1). If ['dep2'] return true if dep2|dep4. So there should at least one match of at least one element from p.1 and recursive result from p.5. I hope I illustrated it in the clearest way possible
Almost forgot - the query above gives me
"Column "parent_id" for "cte" is specified more than once"
But I don't think that it does what it's supposed to do, I need to rewrite it
Any help would be greatly appreciated
Without some sample data (and parameter values) and expected output for that data (with those parameter values), it's difficult to verify this solution.
I have assumed that your tbl_ou and tbl_department are in fact the same table.
Other than the CTE, it looks like the rest of your code should work. The CTE below now travels "both" directions through the hierarchy (upwards and downwards), finding both parents and children. Note that it only finds parents of parents and children of children, it doesn't find children of parents, for example, so no "siblings", "uncles" or whatever these records should be called!
You may need to cast both fields in the CTE seed record as the relevant data type. Based on the supplied data I have assumed that the datatype for department id (and therefore also for parent_id) is varchar(10).
cte as (
select
cast(e.parent_id as varchar(10)) as id,
cast(o.parent_id as varchar(10)) as parent_id,
0 as iteration
from
reqEmployees r
join tbl_employee e on e.id = r.employee_id
join tbl_department o on e.parent_id = o.id
--extra table here compared to earlier versions to allow us
--to traverse hierarchy in both directions
union all
select --This one finds "child" departments
o.id,
o.parent_id,
cte.iteration + 1
from
cte
join tbl_department o on o.id = cte.parent_id
where
cte.iteration >=0 --prevents siblings/uncles etc
union all
select --This one finds "parent" departments
o.id,
o.parent_id,
cte.iteration - 1
from
cte
join tbl_department o on o.parent_id = cte.id
where
cte.iteration <=0 --prevents siblings/uncles etc
)
You can test my script using this SQL Fiddle (updated).

SQL Server : replace several instances of userID with username

I may be overthinking this but I have not managed to figure it out or find a solution, so I'm hoping for a pointer in the right direction. I tried using the Select ColumnA AS Column B etc but it's not doing what I want.
I have 2 tables, scenario examples below
Table 1 (Vehicle)
VehicleID (001)
VehicleMake (Ford)
VehicleModel (Falcon)
VehicleExCleanEmpID (005)
VehicleIntCleanEmpID (003)
Table 2 (Employee)
EmpID (005)
EmpName (Dave)
The scenario being that a vehicle is cleaned internally or externally by any one of a pool of employees shown by the relevant ID in the Vehicles table.
I want to show in a query VehicleID, InsideCleanName, ExternalCleanName rather than showing the employee's ID.
So end up with results similar to this
VehicleID InsideCleanName ExternalCleanName
------------------------------------------------
001 Bob Dave
002 Sue Dave
003 John Sid
Thanks for any tips and or help
THat seems like a pretty simple query with two inner joins to the Employee table - something like this:
SELECT
v.VehicleId,
InsideCleanName = e1.EmpName,
ExternalCleanName = e2.EmpName
FROM
dbo.Vehicle v
INNER JOIN
dbo.Employee e1 ON v.VehicleIntCleanEmpId = e1.EmpID
INNER JOIN
dbo.Employee e2 ON v.VehicleExCleanEmpId = e2.EmpID
Joining to the Employee e1 table is giving you the employee who was responsible for the inside cleaning, while joining a second time, to Employee e2 gives you the one responsible for the external cleaning.
Join the two table with EmpId and select the columns you want similar to the code below:
select column1, column2 from table1 inner join table2 on table1.EmpId = table2.EmpId

How to self JOIN recursively in SQL?

I have a table:
Series
========
ID
SeriesName
ParentSeriesID
A series can be a "root" series, (ParentSeriesID is 0 or null) or it can have a Parent. A series can also be several levels down, i.e. its Parent has a Parent, which has a Parent, etc.
How can I query the table to get a Series by it's ID and ALL descendant Series' ?
So far I have tried:
SELECT child.*
FROM Series parent JOIN Series child ON child.ParentSeriesID = parent.ID
WHERE parent.ID = #ParentID
But this only returns the first level of children, I want the parent node, and all "downstream" nodes. I am not sure how to progress from here.
If you are using SQL Server 2005+, you can use common-table expressions
With Family As
(
Select s.ID, s.ParentSeriesId, 0 as Depth
From Series s
Where ID = #ParentID
Union All
Select s2.ID, s2.ParentSeriesId, Depth + 1
From Series s2
Join Family
On Family.ID = s2.ParentSeriesId
)
Select *
From Family
For more:
Recursive Queries Using Common Table Expressions
I just enhance the work of Thomas. If you need to get the depth of the hierarchy and getting the parentid here is the code.
This was almost the same with Thomas' work.
With Family As
(
Select s.ID, s.ParentSeriesId, 0 as Depth
From Series s
Where ID = #ParentID <--- this was removed if you intend to get all hierarchy of the record. You can retain this if you want
Union All
Select s2.ID, s2.ParentSeriesId < --- change to **Family.ParentID**, Depth + 1
From Series s2
Join Family
On Family.ID = s2.ParentSeriesId
)
Select *
From Family
That's all. I know it's too late but I hope anyone who encounter this may help them. Thanks Thomas for the original code. :)
Make use of CTE feature avaiable in slq server 2005 onwards for recurisve query
USE AdventureWorks
GO
WITH Emp_CTE AS (
SELECT EmployeeID, ContactID, LoginID, ManagerID, Title, BirthDate
FROM HumanResources.Employee
WHERE ManagerID IS NULL
UNION ALL
SELECT e.EmployeeID, e.ContactID, e.LoginID, e.ManagerID, e.Title, e.BirthDate
FROM HumanResources.Employee e
INNER JOIN Emp_CTE ecte ON ecte.EmployeeID = e.ManagerID
)
SELECT *
FROM Emp_CTE
GO
You can view example over here :
SQL SERVER – Simple Example of Recursive CTE