SQL Joins and Corelated subqueries with column data - sql

I am facing an issue in terms of understanding the joins. Lets say for an example we have two tables employee and sales and now I have a query where we have sales of an employee using the id of the employee
select e.employeename
,s.city
,SUM(s.sales)
from employee e
left join (select sales,eid from sales) s on s.eid = e.id
group by 1,2
I'd like to understand why s.city wasn't showing up? and also would like to understand what is this concept called? Is it co related sub queries on Joins? Please help me down over here.

select
e.employeename
,s.city
,SUM(s.sales)
from employee e
left join (select sales,eid,city from sales) s on s.eid = e.id
group by 1,2
in the left join above you have to add city as well. The query Imagine select sales,eid,city from sales is a table itself and then from this table you are selecting city (your second column s.city) this will run error as your table doesn't have a city column yet.
It is much easier to use CTE (common table expressions than CTE's) You can also do the above question as
select
e.employeename
,s.city
,SUM(s.sales)
from employee e
left join sales as s
on e.id = s.id
group by 1,2
here I have added e.id = s.id instead of s.id = e.id it is better to reference the key of the main table first.
you could use CTE (although used when you have to do a lot of referencing but you can see how it works):
With staging as (
select
e.employeename
,s.city
,s.sales
from employee e
left join sales as s
on e.id = s.id
),
sales_stats as (
select
staging.employeename,
staging.city,
sum(staging.sales)
from staging
group by 1,2
#here you will select from staging again consider staging as a separate table so you will have to have all the columns in the staging that you want to use further. Also you will have to reference columns using staging.x
)
select * from sales_stats
-- here you could have combined the steps but I wanted to show you how cte works, Hope this works for you

Related

Single SQL query on many to many relationship using join

I have a simple database with few tables (and some sample columns)
Employee (ID, Title, Content)
Project (ID, Title)
ProjectEmployee(ID ,EMPLOYEE_ID,PROJECT_ID)
Need to find employee's whose don't have any project using join query
I am able to do it using subquery
select * from employee
where id not in (select id from project_employee);
The LEFT JOIN with NOT NULL give you the expected results.
select e.*
from employee e
LEFT JOIN project_employee pe on pe.id = e.id
where pe.id is not null;
This would be best implemented using not exists, joining is not the correct solution if you do not need to return any columns from the joined table.
select *
from employee e
where not exists (select * from project_employee pe where pe.id = e.id)

Struggling with SQL subquery selection

I'm trying to answer a SQL question for revision purposes but can't seem to work out how to get it to work. The tables in question are:
The question is asking me to write an SQL command to display for each employee who has a total distance from all journeys of more than 100, the employee's name and the total number of litres used by the employee on all journeys (the number of litres for a journey is distanceInKm / kmPerLitre).
So far I've tried several variations of code beginning with:
SELECT
name, TravelCost.distanceInKm / Car.kmPerLitre AS "Cost in Litres"
FROM
Employee, Car, TravelCost
WHERE
Employee.id = TravelCost.employeeID
AND Car.regNo = TravelCost.carRegNo
It's at this point I get a bit stuck, any help would be greatly appreciated, thanks!
Never use commas in the FROM clause. Always use proper, standard, explicit JOIN syntax.
You are missing a GROUP BY and a HAVING:
SELECT e.name, SUM(tc.distanceInKm / c.kmPerLitre) AS "Cost in Litres"
FROM Employee e JOIN
TravelCost tc
ON e.id = tc.employeeID JOIN
Car c
ON c.regNo = tc.carRegNo
GROUP BY e.name
HAVING SUM(tc.distanceInKm) > 100;
Use Group By and Having Clause
SELECT NAME,
Sum(TravelCost.distanceInKm/ Car.kmPerLitre) AS "Cost in Litres"
FROM Employee
INNER JOIN TravelCost
ON Employee.id = TravelCost.employeeID
INNER JOIN Car
ON Car.regNo = TravelCost.carRegNo
GROUP BY NAME
HAVING Sum(distanceInKm) > 100
You need to JOIN all the tables and find sum of litres like this:
select
e.*,
sum(distanceInKm/c.kmPerLitre) litres
from employee e
inner join travelcost t
on e.id = t.employeeId
inner join car c
on t.carRegNo = c.regNo
group by e.id, e.name
having sum(t.distanceInKm) > 100;
Also, you need to group by id instead of just names as the other answers suggest. There can be multiple employees with same name.
Also, use explicit JOIN syntax instead of older comma based syntax. It's modern and clearer.
-- **How fool am I! How arrogant am I! I just thought `sum(tc.distanceInKm/c.kmPerLitre)`
-- may have a problem, since a employee may have multiple cars,and car's kmPerLitre is differenct.
-- However there is no problem, it's simple and right!
-- The following is what I wrote, what a bloated statement it is! **
-- calcute the total number of litres used by the employee on all journeys
select e.name, sum(Cost_in_Litres) as "Cost in Litres"
from (
select t.employeeID
-- calcute the litres used by the employee on all journeys group by carRegNo
, sum(t.distanceInKm)/avg(c.kmPerLitre) as Cost_in_Litres
from TravelCost t
inner join Car c
on c.regNo = t.carRegNo
where t.employeeID in
( -- find the employees who has a total distance from all journeys of more than 100
select employeeID
from TravelCost
group by employeeID
having sum(distanceInKm)> 100
)
group by t.carRegNo, t.employeeID
) a
inner join Employee e
on e.id = a.employeeID
group by e.id,e.name;

Joining two tables with specific columns

I am new to SQL, I know this is really basic but I really do not know how to do it!
I am joining two tables, each tables lets say has 5 columns, joining them will give me 10 columns in total which I really do not want. What I want is to select specific columns from both of the tables so that they only show after the join. (I want to reduce my joining result to specific columns only)
SELECT * FROM tbEmployees
JOIN tbSupervisor
ON tbEmployees.ID = tbSupervisor.SupervisorID
The syntax above will give me all columns which I don't want. I just want EmpName, Address from the tblEmployees table and Name, Address, project from the tbSupervisor table
I know this step:
SELECT EmpName, Address FROM tbEmployees
JOIN tbSupervisor
ON tbEmployees.ID = tbSupervisor.SupervisorID
but I am not sure about the supervisor table.
I am using SQL Server.
This is what you need:
Select e.EmpName, e.Address, s.Name, S.Address, s.Project
From tbEmployees e
JOIN tbSupervisor s on e.id = SupervisorID
You can read about this on W3Schools for more info.
You can get columns from specific tables, either by their full name or using an alias:
SELECT E.EmpName, E.Address, S.Name, S.Address, S.Project
FROM tbEmployees E
INNER JOIN tbSupervisor S ON E.ID = S.SupervisorID
You can use the table name as part of the column specification:
SELECT tbEmployees.EmpName, tbEmployeesAddress, tbSupervisor.Name,
tbSupervisor.Address, tbSupervisor.project
FROM tbEmployees
JOIN tbSupervisor
ON tbEmployees.ID = tbSupervisor.SupervisorID
SELECT employees.EmpName, employees.Address AS employeer address,
supervisor.Name, supervisor.Address AS supervisor address,supervisor.project
FROM tbEmployees
AS employees
JOIN tbSupervisor
AS supervisor
ON
employees.ID = supervisor.SupervisorID
You need to learn about aliases. They will make your queries more maintainable. Also, you should always use aliases when referencing columns, so your query is clear about what it is doing:
SELECT e.EmpName, e.Address, s.name, s.address as SupervisorAddress
FROM tbEmployees e JOIN
tbSupervisor s
ON e.ID = s.SupervisorID;
Note that I also renamed the second address so its name is unique.
Specify the table name and field name in your selection
SELECT tbEmployees.EmpName,
tbEmployees.Address,
tbSupervisor.[column name]
FROM tbEmployees
JOIN tbSupervisor ON tbEmployees.ID = tbSupervisor.SupervisorID
SELECT product_report.*,
product.pgroup
FROM `product_report`
INNER JOIN product
ON product_report.product_id = product.id
WHERE product.pgroup = '5'
ORDER BY product.id DESC

Right Outer join issue

I've two tables that I want to join and filter data from. I used a stored procedure to do that. My intention is to bring every item from the second table (i.e Department) even if they don't have a matching record in the first table (i.e. Employee) and finally display the count. Here is the segment of the code I used:
select d.deptName,
case when COUNT(*) is null then '0' else count(*) end AS total
from Employee e
right outer join Department d on e.deptID=d.deptID
WHERE e.Year=#year
and e.Month=#month
group by d.deptName
order by d.deptName
But, it's not displaying what i wanted and failed to figure out the real problem.
When you apply the filter condition through where clause after join, it filters out all the records which doesn't satisfy the filter criteria. Try moving your filter criteria in join condition itself as below:
select d.deptName,
case when COUNT(*) is null then '0' else count(*) end AS total
from Employee e
right outer join Department d
on (e.Year=#year
and e.Month=#month
and e.deptID=d.deptID)
group by d.deptName
order by d.deptName
I think you need to change the code like this
SELECT d.deptName, COUNT(e.deptID) AS total
FROM Employee e
RIGHT OUTER JOIN Department d
ON (e.Year= #year
AND e.Month= #month
AND e.deptID=d.deptID)
GROUP BY d.deptName
ORDER BY d.deptName
See the SQL Fiddle for the query : http://sqlfiddle.com/#!3/b1105/17

How to select records with no matches in the foreign table (Left outer join)

I have one table that holds my ressources:
Ressource | Ressource-ID
And a table that holds the associations
Ressource-ID | Employee-ID
How to select the ressources of an Employee that are available, i.e. not in the association table?
I've tried this, but it's not working:
select r.ress, r.ress_id
FROM Ressource r
LEFT outer JOIN Ressource_Employee_Association a ON r.ress_id = a.ress_id
WHERE a.emp_id = 'ID00163efea66b' and a.ress_id IS NULL
Any ideas?
Thanks
Thomas
After writing my above comments, and looking at the proposed solutions: I think I've got some more understanding of what you are trying to do.
Assuming you have unlimited quantity of resources in your resources table, you want to select the un-assigned resources per employee (based on their non-existence for any specific employee in the resource association table).
In order to accomplish this (and get a comprehensive list of employees) you'll need a 3rd table, in order to reference the complete list of employees. You'll also need to CROSS JOIN all of the resources onto the list of employees (assuming every employee has access to every resource), then you LEFT JOIN (LEFT OUTER JOIN whatever) your association list onto the query where the resource_id and employee_id match the resource_id in the resources table, and the employee_id in the employees table (respectively). THEN you add your where clause that filters out all the records that assign an employee to a resource. This leaves you with the resources that are available to the employee, which they also do not have signed out. This is convoluted to say, so hopefully the query sheds more light:
SELECT e.employee_id, e.employee, r.res_id, r.res
FROM employees e
CROSS JOIN resources r
LEFT JOIN assigned_resources ar
ON ar.employee_id = e.employee_id AND r.res_id = ar.res_id
WHERE ar.res_id IS NULL
If you don't have an employees table, you can accomplish the same by using the assigned resources table, but you will be limited to selecting employees who already have some resources allocated. You'll need to add a GROUP BY query because of the possible existence of multiple employee definitions in the association table. Here's what that query would look like:
SELECT e.employee_id, r.res_id, r.res
FROM assigned_resources e
CROSS JOIN resources r
LEFT JOIN assigned_resources ar
ON ar.employee_id = e.employee_id AND r.res_id = ar.res_id
WHERE ar.res_id IS NULL
GROUP BY e.employee_id, r.res_id
Does this work?
select r.ress, r.ress_id
from resource r
where not exists
(
select 1 from ressource_emplyee_association a
where a.emp_id = '...' and a.ress_id = r.ress_id
)
EDIT
Before that I had the following, but changed it according to the comments below:
select r.ress, r.ress_id
from resource r
where not exists
(
select top 1 1 from ressource_emplyee_association a
where a.emp_id = '...' and a.ress_id = r.ress_id
)
The WHERE clause is applied after the LEFT JOIN. This means that you are currently trying to get results where there is NO matching record in Ressource_Employee_Association, but where the emp_id equals 'ID00163efea66b'.
But if there is no matching record, how can emp_id be anything other than NULL?
One option is to move part of the WHERE clause into the join...
SELECT
r.ress, r.ress_id
FROM
Ressource r
LEFT OUTER JOIN
Ressource_Employee_Association a
ON r.ress_id = a.ress_id
AND a.emp_id = 'ID00163efea66b'
WHERE
a.ress_id IS NULL
This will list all resources that are not associated to employee 'ID00163efea66b'.
EDIT
Your comment implies that what you want is...
- A view listing all employees
- For each employee list each resource that they DON'T have
This requires an extra table listing all of your employees.
SELECT
*
FROM
Employee
CROSS JOIN
Ressource
WHERE
NOT EXISTS (SELECT * FROM Ressource_Employee_Association
WHERE emp_id = Employee.id
AND ress_id = Ressource.id)
SELECT *
FROM Ressource
WHERE ress_id IN (
SELECT ress_id,
FROM Ressource
MINUS
SELECT ress_id
FROM Ressource_Employee_Association
WHERE emp_id = 'ID00163efea66b'
);