TSQL basic join on Northwind database - sql

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!

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.

Creating a table view for invoice - Northwind

I have a little problem with my last tasks. I am using an old Northwind database.
First, I had to create a query, that will give me all the important information for the invoice. My query looks like this:
SELECT b.OrderID,
b.CustomerID,
c.CompanyName,
c.Address,
c.City,
c.PostalCode,
c.CountryID as CustomersCountryID,
concat(d.FirstName, ' ', d.LastName) as Salesperson,
a.CompanyName as ShippingVia,
e.ProductID,
f.ProductName,
e.Quantity,
e.UnitPrice * e.Quantity * (1 - e.Discount) as ExtendedPrice
from Shippers a
inner join Orders b on a.ShipperID = b.ShipVia
inner join Customers c on c.CustomerID = b.CustomerID
inner join Employees d on d.EmployeeID = b.EmployeeID
inner join [Order Details] e on b.OrderID = e.OrderID
inner join Products f on f.ProductID = e.ProductID
order by b.OrderID
It works, it gives me all the orders made with informations. But now, I need to create a table view for an invoice of particular OrderId. When I write something like this:
CREATE VIEW FAKTURA AS
SELECT b.OrderID,
b.CustomerID,
c.CompanyName,
c.Address,
c.City,
c.PostalCode,
c.CountryID as CustomersCountryID,
concat(d.FirstName, ' ', d.LastName) as Salesperson,
a.CompanyName as ShippingVia,
e.ProductID,
f.ProductName,
e.Quantity,
e.UnitPrice * e.Quantity * (1 - e.Discount) as ExtendedPrice
from Shippers a
inner join Orders b on a.ShipperID = b.ShipVia
inner join Customers c on c.CustomerID = b.CustomerID
inner join Employees d on d.EmployeeID = b.EmployeeID
inner join [Order Details] e on b.OrderID = e.OrderID
inner join Products f on f.ProductID = e.ProductID
WHERE b.OrderID = 10248
I am just creating a separate view file for that particular OrderID. It doesn't look like a real life invoice at all.
It should resemble something like this:
I need to separate general data about invoice and customer from data about order itself, product ID, quantity etc.
Is it possible to create something similar in SQL Server Management Studio? How can I do it?
Your query already provides the header (customer info) and table body parts, so what you could do is (NOTE: this is NOT what I would do but, considering the need to have a very simple solution):
Phrase a query that would perform all the needed calculations for the trailing part based on the INVOICE number (this can be similar to what you already have except that it would SUM the amounts, calculate taxes and shipping cost),
Once you have that query ready, INNER JOIN it to the one you already have (using the invoice ID as a key); Note that you will have to add the additional fields to the top SELECTs list.
The result will be what you already have + the subtotal, tax, shipping and total in each and every record.
Again, this is NOT the most efficient and elegant solution, but matches your needs (simplicity and final result).

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

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.

SQL-Server, confront Customers with Employees function

I might have a really simple question, but it doesn't seem i can find the solution yet. Basically, i have to write a SQL-Server function, based on Northwind DB. It has to:
Take 2 dates as arguments and display, without repetition these Customer's data ID | Name | City | Address , for those customers where the total purchase he had made from at least one Employee , is greater than the average sale made by this Employee between the two dates.
So the main steps should be:
1. Retrieve the total purchase made from a Customer from each Employee. I know how to get the total purchase from each Company:
SELECT
Customers.CompanyName, SUM(UnitPrice*Quantity)
FROM Orders inner join [Order Details]
ON Orders.OrderID=[Order Details].OrderID INNER JOIN
Customers ON Orders.CustomerID=Customers.CustomerID
GROUP BY Customers.CompanyName
But how can i get those made from each employee?
2.Confront this with the average sales of this Employee between the given dates. I can get the average for each employee:
SELECT FirstName+' '+LastName, AVG(UnitPrice*Quantity)
FROM Orders inner join [Order Details]
ON Orders.OrderID=[Order Details].OrderID
INNER JOIN Employees
ON Orders.EmployeeID=Employees.EmployeeID
WHERE OrderDate BETWEEN #dt1 and #dt2
GROUP BY FirstName+' '+LastName
Note that i'm only pasting the query part, but here, the Employee should depend on the first query (probably this should be put inside a subquery)
Everything should be put inside a single function (it should not be split in two). The Northwind DB diagram is: Northwind Diagram . Please help!
Hope I got the logic right:
create function x (#from datetime, #to datetime)
returns table
as
return (
with cust as (
select o.customerid, o.employeeid, sum(unitprice*quantity) as cust_purchase
from orders o
inner join [order details] d on o.orderid=d.orderid
where o.orderdate between #from and #to
group by o.customerid, o.employeeid
),
emp as (
select o.employeeid, avg(unitprice*quantity) as emp_sale
from orders o
inner join [order details] d on o.orderid=d.orderid
where o.orderdate between #from and #to
group by o.employeeid
)
select c.customerid, c.companyname, c.city, c.address
from cust
inner join emp on cust.employeeid = emp.employeeid
and cust.cust_purchase > emp.emp_sale
inner join customers c on cust.customerid = c.customerid
)
go
select * from x ('19980401', '19980430')