SQL Join condition clause - sql

I have database with as diagram shows (see picture below)
My task is to show total value of orders handled by each Employees.
I have SQL statement:
SELECT e.FirstName,
e.LastName,
SUM(od.Quantity * od.UnitPrice * (1-od.Discount))
FROM Orders AS o
JOIN Employees AS e
ON o.EmployeeID = e.EmployeeID
JOIN [Order Details] AS od
ON o.OrderID = od.OrderID
GROUP BY e.FirstName,e.LastName
I have problem with further steps. I need to limited results only for those employees which:
A) Have employees under them
B) Don't have any employees under them
I know It concern field ReportsTO in Employees table, but I don't know how get proper SQL clause. I am supposed to do with "EXISTS" or self-join ?
Thank You.

Yes, Just use Exists.
Where Exists(Select 1 from Employees where ReportsTo = e.EmployeeId)

Use a self join to try to find someone reporting to him
SELECT e.FirstName,
e.LastName,
SUM(od.Quantity * od.UnitPrice * (1-od.Discount))
FROM Orders AS o
INNER JOIN Employees AS e
ON o.EmployeeID = e.EmployeeID
LEFT JOIN Employees as under -- self join
ON e.EmployeeID = under.ReportTo
INNER JOIN [Order Details] AS od
ON o.OrderID = od.OrderID
GROUP BY e.FirstName,e.LastName
HAVING MAX(under.ReportTo) IS NULL -- If doesnt find a match mean no one subordinate
-- MAX(under.ReportTo) IS NOT NULL -- mean have at least one subordinate

With a self join you can potentially create duplicates which would be difficult to debug due to the GROUP BY. EXISTS with a correlated sub query in the WHERE clause would be how I would accomplish this.
SELECT *
FROM Employees e
WHERE EXISTS(
SELECT 1
FROM Employees _e
WHERE _e.ReportsTo = e.EmployeeID)
In your query:
SELECT e.FirstName,
e.LastName,
SUM(od.Quantity * od.UnitPrice * (1-od.Discount))
FROM Orders AS o
JOIN Employees AS e
ON o.EmployeeID = e.EmployeeID
JOIN [Order Details] AS od
ON o.OrderID = od.OrderID
WHERE /*NOT*/ EXISTS(
SELECT 1
FROM Employees _e
WHERE _e.ReportsTo = e.EmployeeID)
GROUP BY e.FirstName,e.LastName

Related

Give the names of employees who sell the products of more than 10 suppliers

I am finding it difficult to write the query for the above question I have written the following query but it is returning all the rows
SELECT e.EmployeeID
FROM employees e
JOIN orders o ON e.EmployeeID = o.EmployeeID
JOIN order_details od ON o.OrderID = od.OrderID
JOIN Products p ON od.ProductID = p.ProductID
GROUP BY e.EmployeeID
HAVING COUNT(p.SupplierID) > 10
Your filtering condition is:
HAVING COUNT(p.SupplierID) > 10
COUNT() counts the number of non-NULL values. Presumably, all products have a supplier and you are using inner joins. So, all matching rows get counted. It is equivalent to:
HAVING COUNT(*) > 10
And this counts the number of order lines for a given employee.
You want to count distinct suppliers, not order lines. The simplest method is to use COUNT(DISTINCT):
HAVING COUNT(DISTINCT p.SupplierID) > 10
Because you are learning SQL, I would advise you to understand this version as well:
SELECT e.EmployeeID
FROM (SELECT e.EmployeeID, p.SupplierID, COUNT(*) as num_ordelines
FROM employees e JOIN
orders o
ON e.EmployeeID = o.EmployeeID JOIN
order_details od
ON o.OrderID = od.OrderID JOIN
Products p
ON od.ProductID = p.ProductID
GROUP BY e.EmployeeID, p.SupplierId
) es
GROUP BY EmployeeID
HAVING COUNT(SupplierID) > 10
This returns the same result set (assuming SupplierId is never NULL). The subquery has one row per employee/supplier. The outer query then counts these rows.

Rewrite SQL Code Using a Join Instead of Subquery

I'm having trouble visualizing how subqueries would look in the form of joins instead. In particular, the following SQL:
SELECT DISTINCT e.EmployeeID, e.LastName, e.FirstName
FROM Employees e
INNER JOIN Orders o ON e.EmployeeID = o.EmployeeID
WHERE EXISTS
(
SELECT c.Country
FROM Customers c
WHERE c.Country = e.Country
)
It would greatly be appreciated if I could receive some tips on what to do when I want to turn a statement with a subquery into a join instead.
You need to be careful about duplicates, but the transformation for EXISTS is quite direct:
SELECT DISTINCT e.EmployeeID, e.LastName, e.FirstName
FROM Employees e INNER JOIN
Orders o
ON e.EmployeeID = o.EmployeeID INNER JOIN
(SELECT DISTINCT c.Country
FROM Customers c
) c
ON c.Country = e.Country
This looks clean. In fact, it is not:
SELECT DISTINCT e.EmployeeID, e.LastName, e.FirstName
FROM Employees e
JOIN Orders o ON o.EmployeeID = e.EmployeeID
JOIN Customers c ON c.Country = e.Country
;
The point is that if there are more than one matching rows for orders and Customers, these will all cause a seperate copy of the Employee record to be merged. These will later have to be suppressed by theDISTINCT(the optimiser might catch this, or it might not)
The point is: you dont need a distinct, since you are only selecting columns from employee:
SELECT e.EmployeeID, e.LastName, e.FirstName
FROM Employees e
WHERE EXISTS(
SELECT * FROM Orders o
WHERE o.EmployeeID = e.EmployeeID
)
AND EXISTS(
SELECT * FROM Customers c
WHERE c.Country = e.Country
);
Or,in a more or less ugly style:
SELECT e.EmployeeID, e.LastName, e.FirstName
FROM Employees e
WHERE EXISTS(
SELECT c.Country
FROM Orders o
JOIN Customers c
ON o.EmployeeID = e.EmployeeID
AND c.Country = e.Country
);

Select IDs which do not JOIN

I want to get the list of ProductId which has not been ordered by an Employee.
I have this query which gives me all ProductId which has been ordered, but I want reverse of it:
SELECT distinct e.EmployeeID ,p.ProductID
FROM Products p JOIN OrderDetails od
ON od.ProductID=p.ProductID JOIN Orders o
ON o.OrderID=od.OrderID JOIN Employee e
ON e.EmployeeID=o.EmployeeID
order by e.EmployeeID
I'm not sure to understand your question, but if you want products without Employee associated you can try this :
SELECT distinct e.EmployeeID ,p.ProductID
FROM Products p
INNER JOIN OrderDetails od
ON od.ProductID=p.ProductID
INNER JOIN Orders o
ON o.OrderID=od.OrderID
LEFT JOIN Employee e
ON e.EmployeeID=o.EmployeeID
WHERE e.EmployeeID is NULL
order by e.EmployeeID
The idea is to select all the orders (INNER JOIN) also orders who haven't Employee associated (LEFT JOIN). Finally you filter on those which haven't association (IS NULL).

Northwind: Select EmployeeID where the employee's average amount of sales exceeds the average amount of orders

I am trying to select EmployeeID,FirstName where the average amount of sales of emplyee exceeds the average amount of orders. I calculate the average amount of orders and average amount of sales of each employee but I could not perform the comparision because of subquery returns more than one value.
Average amount of orders:
select SUM((od.UnitPrice*od.Quantity)*(1-Discount))/COUNT(distinct o.OrderID)
from [Order Details] od
join Orders o on o.OrderID=od.OrderID join Employees e on e.EmployeeID= o.EmployeeID
and
this is the amount of average sales for each employee:
select e.FirstName, SUM((od.UnitPrice*od.Quantity)*(1-Discount))/COUNT(distinct o.OrderID) as AmountOfAverageOfSales
from [Order Details] od join Orders o on o.OrderID=od.OrderID join Employees e on e.EmployeeID= o.EmployeeID
group by e.FirstName
I want to reach the EmployeeID,FirstName where amount of average sales of an employee exceeds the average amount of orders but as I said before subquery (second query) returns more then one value so query is not working when I use where. How can I write this query?
I am sorry If I waste time of someone. I have just solved it by using having.
select e.FirstName
from (Orders o inner join Employees e on e.EmployeeID = o.EmployeeID inner join [Order Details] od on o.OrderID=od.OrderID) group by e.FirstName
having SUM((od.UnitPrice*od.Quantity)*(1-od.Discount))/COUNT(distinct o.OrderID) > (select SUM((od.UnitPrice*od.Quantity)*(1-od.Discount))/COUNT(distinct o.OrderID)
from [Order Details] od join Orders o on od.OrderID = o.OrderID)
Not sure if you're needing the AvgOrders per employee or AvgOrders throughout. If it's the former...
select e.*, AvgOrders.AverageOrders, AvgSales.AmountOfAverageOfSales
From Employees e
inner join (select e.EmployeeID, SUM((od.UnitPrice*od.Quantity)*(1-Discount))/COUNT(distinct o.OrderID) as AverageOrders
from [Order Details] od
join Orders o on o.OrderID=od.OrderID
join Employees e on e.EmployeeID= o.EmployeeID
group by e.EmployeeID)) AvgOrders on (e.EmployeeId = AvgOrders.EmployeeId)
inner join (select e.EmployeeID, SUM((od.UnitPrice*od.Quantity)*(1-Discount))/COUNT(distinct o.OrderID) as AmountOfAverageOfSales
from [Order Details] od
join Orders o on o.OrderID=od.OrderID
join Employees e on e.EmployeeID= o.EmployeeID
group by e.EmployeeID) AvgSales on (e.EmployeeId = AvgSales.EmployeeId)
WHERE AvgSales.AmountOfAverageOfSales > AvgOrders.AverageOrders
If it's the latter you could use...
select e.*, AvgOrders.AverageOrders, AvgSales.AmountOfAverageOfSales
From Employees e
inner join (select SUM((od.UnitPrice*od.Quantity)*(1-Discount))/COUNT(distinct o.OrderID) as AverageOrders
from [Order Details] od
join Orders o on o.OrderID=od.OrderID)) AvgOrders on (1=1)
inner join (select e.EmployeeID, SUM((od.UnitPrice*od.Quantity)*(1-Discount))/COUNT(distinct o.OrderID) as AmountOfAverageOfSales
from [Order Details] od
join Orders o on o.OrderID=od.OrderID
join Employees e on e.EmployeeID= o.EmployeeID
group by e.EmployeeID) AvgSales on (e.EmployeeId = AvgSales.EmployeeId)
WHERE AvgSales.AmountOfAverageOfSales > AvgOrders.AverageOrders

TSQL basic join on Northwind database

I am learning TSQL (well, just SQL to tell the truth) and I want to make Employee - Product statistic on Northwind database.
Expected results should be something like:
EmployeeID | ProductID | income
1 | 1 | 990
1 | 2 | 190
1 | 3 | 0
...
For all Employy-Product pairs
My first try is this query:
SELECT E.EmployeeID, OE.ProductID, SUM(OE.ExtendedPrice) as income
FROM [Order Details Extended] OE
JOIN [Orders] O
ON OE.OrderID = O.OrderID
RIGHT OUTER JOIN Employees E
ON E.EmployeeID = O.EmployeeID
GROUP BY E.EmployeeID, OE.ProductID
ORDER BY E.EmployeeID
But I don't get results for all pairs.
What am I doing wrong?
HLGEM missed few columns but I understood what he tried to do.
I came up with this:
SELECT A.employeeid, A.productid, SUM(Oe.ExtendedPrice) AS income
FROM
(SELECT E.Employeeid, P.productid
FROM employees E
CROSS JOIN products P) A
LEFT JOIN [Order Details Extended] OE
ON A.productid = OE.productid
LEFT JOIN [Orders] O
ON OE.OrderID = O.OrderID
GROUP BY A.EmployeeID, A.ProductID
ORDER BY A.EmployeeID, A.ProductID
This returns results for all pairs, but those don't seem right.
For example above query returns as first row:
1, 1, 12788.10
But this query:
SELECT SUM(ODE.ExtendedPrice) FROM [Order Details Extended] ODE
LEFT JOIN [Orders] OD
ON ODE.OrderID = OD.OrderID
WHERE OD.EmployeeID = 1 AND ODE.ProductID = 1
Returns 990.90.
Why?
edit
I got it finally:
SELECT A.EmployeeId, A.ProductId, ISNULL(SUM(Oe.ExtendedPrice), 0) AS income
FROM
(SELECT E.Employeeid, P.productid
FROM [Employees] E
CROSS JOIN [Products] P) A
LEFT JOIN [Orders] O
ON O.EmployeeID = A.EmployeeID
LEFT JOIN [Order Details Extended] OE
ON A.productid = OE.productid AND OE.OrderID = O.OrderID
GROUP BY A.EmployeeID, A.ProductID
ORDER BY A.EmployeeID, A.ProductID
#HLGEM you can copy/paste this solution to your answer so I can accept it.
You could try:
SELECT A.employeeid,A.product_id, SUM(Oe.ExtendedPrice) AS income
FROM
(SELECT E.Employeeid, P.product id
FROM employee E
CROSS JOIN product p) A
LEFT JOIN [Order Details Extended] OE
ON A.EmployeeID = O.EmployeeID
LEFT JOIN [Orders] O
ON OE.OrderID = O.OrderID
GROUP BY A.EmployeeID, OE.ProductID
ORDER BY A.EmployeeID
I switched it to a LEFT JOIN as most people use them instead of right joins and thus they are easier for maintenance.
What results are you missing? It looks like you have used an inner join between Order Details Extended and Orders, so any orders that don't have details will be excluded. It seems logical that you would want this, since you are summing a value in the details table.
Then you do a right outer join with Employees, so you are including all employees, regardless of whether they have any orders. This also makes sense, since it looks as though you are seeing which employees sold which products.
Your query will only give you the data for products that the employees have actually sold. If you are looking for all employee-product combinations, regardless of whether the employee had ever sold that product, you will want to join the Product table as well, although in my muddled morning, I can't think if you would want an outer join or a cross join.
In all, I think your first try is a good one. Pretty complex for a beginner. Nice effort!