Select IDs which do not JOIN - sql

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).

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.

SQL Join condition clause

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

What is 'keyword' is missing from this query?

This is what I have;
SELECT c.customerFN, c.customerEmail, p.productName,
SUM(p.unitsonstock + p.unitsordered) AS "All Units"
FROM customer c
INNER JOIN order o
WHERE c.customerID=o.customerID
INNER JOIN orderDetails d
WHERE o.orderID=d.orderID
INNER JOIN product p
WHERE p.productCode=l.productCode
WHERE orderDate <= '2015-03-15'
ORDER BY productName;
When I enter this the database throws a "missing keyword" error at the fourth line. Could you tell me what it is that I'm missing
Instead of WHERE in line 5, 7 and 9 you need to use ON. You are also using function SUM, but there is no GROUP BY. Change your query like this:
SELECT c.customerFN, c.customerEmail, p.productName,
SUM(p.unitsonstock + p.unitsordered) AS "All Units"
FROM customer c
INNER JOIN order o
ON c.customerID=o.customerID
INNER JOIN orderDetails d
ON o.orderID=d.orderID
INNER JOIN product p
ON p.productCode=l.productCode
WHERE orderDate <= '2015-03-15'
GROUP BY c.customerFN, c.customerEmail, p.productName
ORDER BY p.productName;
JOIN is performed using the ON clause, not with a WHERE:
...
FROM customer c
INNER JOIN order o ON c.customerID=o.customerID
INNER JOIN orderDetails d ON o.orderID=d.orderID
INNER JOIN product p ON p.productCode=d.productCode
WHERE orderDate <= '2015-03-15'
...
The WHERE clause that comes after join should be used like you have it in your query.
Apart from the problem with the JOIN there is also a problem using SUM without grouping. You probably want something like:
SELECT c.customerFN, c.customerEmail, p.productName,
SUM(p.unitsinstock + p.unitsordered) AS "All Units"
FROM customer c
INNER JOIN order o ON c.customerID=o.customerID
INNER JOIN orderDetails d ON o.orderID=d.orderID
INNER JOIN product p ON p.productCode=d.productCode
WHERE orderDate <= '2015-03-15'
GROUP BY customerFN, customerEmail, productName
ORDER BY p.productName;
Use of SUM function implies a GROUP BY clause. Every column selected that is not part of an aggregate function like SUM must be present in the GROUP BY clause.

Join between two master and one detail Table

I am working with Northwind Data Base(Microsoft Example) and i want to know total Sale of each Region.
tables for this example are (Region,Territories,EmployeeTerritories,Employees,Orders and OrderDetails)
And relations are:
Region on to many Territories
Territories on to many EmployeeTerritories
EmployeeTerritories many to one Employees
Employees on to many Orders
Orders one to many OrderDetails
i know that i should to use Sub query but i dont know how.
select
*
from
Region R
inner join Territories T
on R.RegionID=T.RegionID
inner join EmployeeTerritories ET
on T.TerritoryID=ET.TerritoryID
inner join
(
select
E.EmployeeID,
E.FirstName,
E.LastName,
sum(OD.Quantity*OD.UnitPrice) as TotalEmployeeSale
from
Employees E
inner join Orders O
on E.EmployeeID=o.EmployeeID
inner join [Order Details] OD
on o.OrderID=OD.OrderID
Group by
E.EmployeeID,
E.FirstName,
E.LastName
)as ES on ET.EmployeeID=ES.EmployeeID
I use this sql query to calculate Total sale for Each Employee but how can i Caclulate is for Each Region.
Haven't got access to Northwind right now, so this is untested, but you should get the idea...
There's no need to get data about employees if all you want is sales per region. Your subquery is therefore redundant...
select
r.RegionDescription,
sum(OD.Quantity*OD.UnitPrice)
from
Region R
inner join Territories T
on R.RegionID=T.RegionID
inner join EmployeeTerritories ET
on T.TerritoryID=ET.TerritoryID
inner join Employees E
on ET.EmployeeID=E.EmployeeID
inner join Orders O
on E.EmployeeID=o.EmployeeID
inner join [Order Details] OD
on o.OrderID=OD.OrderID
Group by r.RegionDescription
As discussed in the comments, this "double counts" sales where an employee is assigned to more than one region. In many cases, this is desired behaviour - if you want to know how well a region is doing, you need to know how many sales came from that region, and if an employee is assigned to more than one region, that doesn't affect the region's performance.
However, it means you overstate the sales if you add up all the regions.
There are two strategies to avoid this. One is to assign the sale to just one region; in the comments, you say there's no data on which to make that decision, so you could do it on the "lowest regionID) - something like:
select
r.RegionDescription,
sum(OD.Quantity*OD.UnitPrice)
from
Region R
inner join Territories T
on R.RegionID=T.RegionID
inner join EmployeeTerritories ET
on T.TerritoryID=ET.TerritoryID
inner join Employees E
on ET.EmployeeID=E.EmployeeID
inner join Orders O
on E.EmployeeID=o.EmployeeID
inner join [Order Details] OD
on o.OrderID=OD.OrderID
Group by r.RegionDescription
having et.TerritoryID = min(territoryID)
(again, no access to DB, so can't test - but this should filter out duplicates).
Alternatively, you can assign a proportion of the sale to each region - though then rounding may cause the totals not to add up properly. That's a query I'd like to try before posting though!

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!