SQL query returning cartesian product - sql

I have some tables:
Employee: id, name, id_suc, id dep, id_sec
Suc : id_suc, name
Dep : id_dep, id_suc, name
Sec : id_sec, id_dep, id_suc, name
Don't blame on me, this is an existing application, I didn't create the Database and can't touch the structure since there is too much data inside and reports depending on it. I'm just trying to modify a report as asked.
I do a query:
SELECT DISTINCT
s.name as sucurs,
d.name as depart,
c.name as section,
e.name AS emp
FROM
employee e
join suc s on (e.id_suc = s.id_suc)
join dep d on (e.id_dep = d.id_dep)
join sec c on (e.id_sec = c.id_sec)
ORDER BY
sucurs, depart, section, emp
and brings me a cartesian product. I want:
sucurs1, depart1, section1, emp1
sucurs1, depart1, section1, emp2
.....
(then on the report I group by suc, then dep, then sec)
instead, I got:
sucurs1, depart1, section1, emp1
sucurs2, depart1, section1, emp1
and so on. It brings ALL sucurs, ALL depart, ALL section and sometimes duplicated emp.
I'm missing something, but don't know what. Any clues?

Well, you're always joining the tables to only employee - it would seem that Dep is also linked to Suc - so you need a second JOIN condition (join not only on id_dep but also on id_suc!). Table Sec even needs three JOIN conditions since it shares three id's with the Employee table.
SELECT DISTINCT
s.name as sucurs,
d.name as depart,
c.name as section,
e.name AS emp
FROM
employee e
INNER JOIN
suc s ON e.id_suc = s.id_suc
INNER JOIN
dep d ON e.id_dep = d.id_dep AND e.id_suc = d.id_suc
INNER JOIN
sec c ON e.id_sec = c.id_sec AND e.id_suc = c.id_suc AND e.id_dep = c.id_dep
ORDER BY
sucurs, depart, section, emp

Related

How to find levels of managers in self-referenced key

So I have a table employees(ID, MaganerID)
My task is to find 2-nd level manager who is not getting any bonuses(another table)
I'm stuck here. Any advice what should I do next?
SELECT ID
FROM Employees
WHERE ID NOT IN (Select EmployeeID FROM Bonuses)
AND ID IN (Select LeaderID FROM Employees WHERE LeaderID IS NOT NULL)
AND ID NOT IN (Select ID FROM Employees WHERE ID <> LeaderID)
Well since you want a level 2 manager is much more simple:
SELECT DISTINCT E2.ID
FROM Employees E
INNER JOIN Employees E1 ON E1.ID = E.ManagerID
INNER JOIN Employees E2 ON E2.ID = E1.ManagerID
INNER JOIN Bonuses B ON B.EmployeeID = E2.ID AND B.Bonus = 0
You could also use the Recurse option using the WITH keyword if you are using TSQL. If you simply dont want to not exists on the other table just reverse the last bool check on the join.
Presumably by second-level manager you mean where their EmployeeID is somebody else's LeaderID, whose EmployeeID in turn is a third person's LeaderID?
I think currently you'll just get first level managers.
This should work... I've included your final WHERE statement, although you don't explain what that's for. Do you really want to exlude records where the ID is different to the LeaderID?
SELECT L2.ID
FROM Employees E
JOIN Employees L1 on E.LeaderID = L1.ID
JOIN Employees L2 on L1.LeaderID = L2.ID
WHERE L2.ID not in (SELECT EmployeeID from Bonuses)
AND L2.ID NOT IN (Select ID FROM Employees WHERE ID <> LeaderID)

How to use subqueries in oracle

Hello I'm trying to solve this question with a subquery:
Select names, service number, jobs and salaries of
people working in the same city as HAVET. (havet is a name)
And I have only two table the first one is the emp table with the column (noserv, name, job, salaries) and the second one is the SERV table with the column (noserv, nameserv, city)
I know that I have to use a subquery but I don't know how to do it.
Semi-pseudocode (CTE won't work, obviously).
with emp (noserv, name, job, salaries),
serv (noserv, nameserv, city)
-- This is what you're looking for, I presume
select e.*
from emp e join serv s on e.noserv = s.noserv
where s.city = -- subquery returns city where HAVET lives
(select s1.city
from serv s1 join emp e1 on e1.noserv = s1.noserv
where e1.name = 'HAVET'
);
Try this:
-- This is a normal query with a left join
select *
from emp e
left join s on e.noserv = s.noserv
where s.city =
-- get Havet's city from the subquery.
(select s.city
from emp e
left join s on e.noserv = s.noserv
where e.name = 'HAVET')
Try this one, change the column name according to your table and column names.
Select e.name,e.serviceNubmer,e.Job,e.salaries
from emp e,serv s
where
e.noserv = s.noserv
and s.city ='HAVET';

How to combine two columns from different tables that have a similar name but have different values in SQL Server

I have three tables (example) STAFF, STU, EMP.
I want to combine the column EMPID in table STAFF and table EMP into 1 column?
My previous query is like this,
SELECT *
FROM STU s
FULL OUTER JOIN STAFF st ON st.STAFFID = STUID
FULL OUTER JOIN EMP e ON s.STUID = st.EMPID
The result is like this
The expected result is just like the above screenshot, but I want to join EMPID into one column only.
UPDATE:
I tried using this query:
SELECT
stu.stuid, stu.stuname, stu.stucode,
s.staffid, s.staffname, s.staffcode,
emp.empname, emp.empcode,
COALESCE (emp.empid, staff.staffid) AS col
FROM
STU, Staff, EMP
FULL OUTER JOIN
STAFF s ON s.STAFFID = stu.STUID
FULL OUTER JOIN
EMP e ON stu.STUID = s.EMPID
but it displays an error like this
Use below query to get the desired result.
SELECT s.StuID, s.StuName, s.Stucode, st.StaffId, st.StaffName, st.Staffcode, isnull(st.EmpId, e.EmpId) EmpId, e.EmpCode, e.EmpName
FROM STU s FULL outer JOIN
STAFF st
ON st.STAFFID = STUID FULL OUTER JOIN
EMP e
ON s.STUID = st.EMPID
Note: You will get the one emp Id column as needed. If Staff emp id is not null then staff emp id will be displayed else employee emp id will be displayed

Count on a database using Count function SQL

I have the database schema like this
Flights(flno,from,to,distance,departs,arrives,price)
Aircraft(aid,aname,cruisingRange)
Certified(employee,aircraft)
Employees(eid,ename,salary)
Where Flno is primary key and Each route corresponds to a "flno".
So I have this question to answer for the Schema
For each pilot, list their employee ID, name, and the number of routes
he can pilot.
I have this SQL, is this correct? ( I can test, as I dont have data for the database).
select eid, ename, count(flno)
from employees, flights
groupby flno
This is a simple questioin, but as everyone is mentioning you don't have any link between employee and flights. The relationships stop at certified.
You obviously have or will create some relationship. I have written a query that will give you the count taking into account that you will have a many to many relationship between employee and flights. Meaning an employee can have many flights and a single flight can be made by many employees.
Flights(flno,from,to,distance,departs,arrives,price)
Aircraft(aid,aname,cruisingRange)
Certified(employee,aircraft)
Employees(eid,ename,salary)
select
e.eid employee_id,
e.ename employee_name,
count(*)
from
employees e
inner join certified c on
c.employee = e.eid
inner join aircraft a on
a.aid = c.aircraft
inner join aircraft_flights af on -- new table that you would need to create
af.aircraft = a.aid and
inner join flights f on
f.flno = af.flno -- not I made up a relationship here which needs to exist in some for or another
group by
e.eid,
e.ename
I hope this at least shows you how to write a count statement correctly, but you should probably brush up on your understanding of joins.
Hope that helps.
EDIT
Without the relationships and working in your comments you could get the count as below.
select
e.eid employee_id,
e.ename employee_name,
count(*)
from
employees e
inner join certified c on
c.employee = e.eid
inner join aircraft a on
a.aid = c.aircraft
inner join flights f on
f.distance <= a.cruisingRange
group by
e.eid,
e.ename

Representing 'not in' subquery as join

I am trying to convert the following query:
select *
from employees
where emp_id not in (select distinct emp_id from managers);
into a form where I represent the subquery as a join. I tried doing:
select *
from employees a, (select distinct emp_id from managers) b
where a.emp_id!=b.emp_id;
I also tried:
select *
from employees a, (select distinct emp_id from managers) b
where a.emp_id not in b.emp_id;
But it does not give the same result. I have tried the 'INNER JOIN' syntax as well, but to no avail. I have become frustrated with this seemingly simple problem. Any help would be appreciated.
Assume employee Data set of
Emp_ID
1
2
3
4
5
6
7
Assume Manger data set of
Emp_ID
1
2
3
4
5
8
9
select *
from employees
where emp_id not in (select distinct emp_id from managers);
The above isn't joining tables so no Cartesian product is generated... you just have 7 records you're looking at...
The above would result in 6 and 7 Why? only 6 and 7 from Employee Data isn't in the managers table. 8,9 in managers is ignored as you're only returning data from employee.
select *
from employees a, (select distinct emp_id from managers) b
where a.emp_id!=b.emp_id;
The above didnt' work because a Cartesian product is generated... All of Employee to all of Manager (assuming 7 records in each table 7*7=49)
so instead of just evaluating the employee data like you were in the first query. Now you also evaluate all managers to all employees
so Select * results in
1,1
1,2
1,3
1,4
1,5
1,8
1,9
2,1
2,2...
Less the where clause matches...
so 7*7-7 or 42. and while this may be the answer to the life universe and everything in it, it's not what you wanted.
I also tried:
select *
from employees a, (select distinct emp_id from managers) b
where a.emp_id not in b.emp_id;
Again a Cartesian... All of Employee to ALL OF Managers
So this is why a left join works
SELECT e.*
FROM employees e
LEFT OUTER JOIN managers m
on e.emp_id = m.emp_id
WHERE m.emp_id is null
This says join on ID first... so don't generate a Cartesian but actually join on a value to limit the results. but since it's a LEFT join return EVERYTHING from the LEFT table (employee) and only those that match from manager.
so in our example would be returned as e.emp_Di = m.Emp_ID
1,1
2,2
3,3
4,4
5,5
6,NULL
7,NULL
now the where clause so
6,Null
7,NULL are retained...
older ansii SQL standards for left joins would have been *= in the where clause...
select *
from employees a, managers b
where a.emp_id *= b.emp_id --I never remember if the * is the LEFT so it may be =*
and b.emp_ID is null;
But I find this notation harder to read as the join can get mixed in with the other limiting criteria...
Try this:
select e.*
from employees e
left join managers m on e.emp_id = m.emp_id
where m.emp_id is null
This will join the two tables. Then we discard all rows where we found a matching manager and are left with employees who aren't managers.
Your best bet would probably be a left join:
select
e.*
from employees e
left join managers m on e.emp_id = m.emp_id
where
m.emp_id is null;
The idea here is you're saying that you want to select everything from employees, including anything that matches in the manager table based on emp_id and then filtering out the rows that actually have something in the manager table.
Use Left Outer Join instead
select e.*
from employees e
left outer join managers m
on e.emp_id = m.emp_id
where m.emp_id is null
left outer join will preserve the rows from m table even if they do not have a match i e table based on the emp_id field. The we filter on where m.emp_id is null - give me all the rows from e where there's no matching record in m table.
A bit more on the subject can be found here:
Visual representation of joins
from employees a, (select distinct emp_id from managers) b implies cross join - all posible combinations between tables (and you needed left outer join instead)
The MINUS keyword should do the trick:
SELECT e.* FROM employees e
MINUS
Select m.* FROM managers m
Hope that helps...
select *
from employees
where Not (emp_id in (select distinct emp_id from managers));