SQL Comparison query - sql

I am doing practice queries to improve my SQL skills (I'm a beginner) and I ran into this problem that I need help with using the Northwind database. The requested query should:
Give the name of employees and the city where they live for employees who have
sold to customers in the same city.
What I wrote for this was:
USE Northwind;
SELECT DISTINCT FirstName, LastName, e.City
FROM Employees e
INNER JOIN Orders o ON e.EmployeeID = O.EmployeeID
INNER JOIN Customers c ON o.CustomerID = c.CustomerID
INNER JOIN Customers ON c.City = e.City
WHERE e.City = ANY(SELECT Customers.City FROM Customers);
I am returned 6 employees but I am not sure that they are correct and I believe that my WHERE statement is the wrong one as well.
Basically my question is, am I using the correct join statements and how do I go about filtering results with the WHERE statement? I'm not exactly sure how to compare one specific record with other records. Coming from a Java background I am used to for-loops that can check each individual "object" (record) with a specific field from another "object". In this case I am wondering how I can check the City attribute of each record from the Employees table with the City attribute of the records on the Customers table. Any and all advice is appreciated, thanks!

I think you only need to join the customer table once and have both requirements (being on the same order and same city as the employee) as your join requisites e.g
SELECT DISTINCT FirstName, LastName, e.City
FROM Employees e
INNER JOIN Orders o ON e.EmployeeID = O.EmployeeID
INNER JOIN Customers c ON o.CustomerID = c.CustomerID AND c.City = e.City
Alternatively you can just join the customer on the order id and filter the city requirement in the where clause. Performance-wise there shouldn't be any difference however if you are going to look back on the script at some point it may help you to remember
SELECT DISTINCT FirstName, LastName, e.City
FROM Employees e
INNER JOIN Orders o ON e.EmployeeID = O.EmployeeID
INNER JOIN Customers c ON o.CustomerID = c.CustomerID
WHERE e.City = C.City
I think the important part to get your head round is that after you have joined the table it is effectively one table with columns from both (or more with multiple joins). The inner join conditions will filter out rows that do not have a match between the two tables and then you are left to compare columns

Related

How to get the Total Amount Each Customer Has Spent

I am making a database for a simple purchase, customer, employee, etc tables.
I am making a query that includes all of the employees who have boughten something, along with some of their data, and then the last field is supposed to have the total amount that they have boughten. However, I am not too sure on how to achieve this.
I am not quite sure on how to get the total to be almost like a sub-query to total up what the customer has gotten. For instance, the first 5 rows should have a total of 180.40.
I have been looking online for some help with totaling queries but couldn't find any good examples.
Any help would be wonderful! Thank you (I am pretty new to SQL and Access)!
Edit: Forgot to add this!
SELECT Employee.FirstName, Employee.LastName, Purchase.PurchaseID, Product.ProductName, Product.Price, Product.Price AS Total
FROM Employee
INNER JOIN (Customer INNER JOIN (Product INNER JOIN Purchase ON Product.ProductID = Purchase.ProductID)
ON Customer.CustomerID = Purchase.CustomerID) ON Employee.EmployeeID = Customer.EmployeeID;
Do you just want aggregation?
SELECT e.FirstName, e.LastName,
SUM(p.Price)
FROM ((Employee as e INNER JOIN
Customer as c
ON e.EmployeeID = c.EmployeeID
) INNER JOIN
Purchase pu
ON c.CustomerID = pu.CustomerID
) INNER JOIN
Product as p
ON p.ProductID = pu.ProductID
GROUP BY e.FirstName, e.LastName;

SQL Server inner join returns the same row repeatedly in all the column

I am writing this query to do an inner join:
select firstname
from employees
inner join orders on Employees.employeeid = orders.employeeid
The result is the same row in all the columns.
What is wrong?
Presumably, you have more orders than you have employees. Hence, one employee is on many orders.
When you run:
select e.firstname
from employees e inner join
orders o
on e.employeeid = o.employeeid;
Then you are getting a list of all first names -- so one employee name is going to be repeated, once per each order. If you want just a list of the distinct values, then you can use select distinct instead.
Because of more rows are related to that employee in orders table,
it may be help to understand:
SELECT Employees.firstname,Employees.employeeid,
orders.orderid,orders.employeeid
FROM employees
INNER JOIN orders
ON Employees.employeeid=orders.employeeid

Returning data for the highest total in a group

I have these tables:
customers, which includes: employee id of the employee that helped them
orders, which includes: order number, product ID, shipping date, customer name who ordered that item
orderdetails, which includes order number, quantity, product ID
employee, which includes first name, last name
How could I show the last name and first name of the employee with the highest quantity of products sold?
I know I have to use multiple joins, aliases, and a few HAVING statements, but I'm not sure how to proceed.
You won't need HAVING at all. As you've suggested, join all the tables on the join keys, and then group by both of the employee name columns.
The below query (with derived table) should be agnostic to any RDBMS, but excludes the final consideration of limiting the result to one row only, which is dependent on the RDBMS
SELECT d.FirstName, d.LastName, d.TotalQuantity
FROM
(
SELECT e.FirstName, e.LastName, SUM(od.Quantity) AS TotalQuantity
FROM OrderDetails od
INNER JOIN Orders o ON OrderDetails.OrderId = o.OrderId
INNER JOIN Customers c ON o.CustomerName = c.CustomerName -- Switch to a surrogate key
INNER JOIN Employees e ON c.EmployeeId = e.EmployeeId
-- WHERE od.ProductId = `FooBars` -- If you need a filter
GROUP BY e.FirstName, e.LastName
) d
ORDER BY d.TotalQuantity DESC;
For the 'just the top employee' requirement, you'll need to limit the row count to just 1, which is RDBMS dependent:
Change last line to ORDER BY ... LIMIT 1; for MySql
or Change last line to ORDER BY ... FETCH FIRST 1 ROWS ONLY; for newer versions of SqlServer and Oracle
or Change first line to SELECT top 1 d.FirstName, .. for older versions of SqlServer
Notes:
As per the comment, it seems strange that the product id is on the Orders table, sku / item code would typically be at the order item level
Modelling the serving employee id directly on the customer table isn't a good idea - what about repeat customers? A new ServingEmployeeCustomer junction table sounds in order.
Joining on CustomerName is also not a great idea - if there is no natural key, recommend generating a simple surrogate key (INT identity / auto number, or a Guid, etc)
Try this:
SELECT e.FirstName, e.LastName, SUM(od.Quantity) AS Quantity
FROM OrderDetails od
INNER JOIN Orders o ON od.OrderId = o.OrderId
INNER JOIN Customers c ON o.CustomerName = c.CustomerName
INNER JOIN Employees e ON c.EmployeeId = e.EmployeeId
GROUP BY e.FirstName, e.LastName
ORDER BY Quantity DESC;

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!

Using an inner select construction for a select value

I would like to hear if anyone can tell me a simple syntax that accomplishes the same as the following (with the same flexibility):
SELECT C.CompanyName,
(SELECT Count(*) FROM Employees WHERE CompanyId = C.Id) as EmployeeCount
FROM Company C
Now, what's important is that the inner SELECT giving the EmployeeCount is:
An independent SELECT statement
This means that it should work with any existing SELECT, even if it already contains joins etc.
Can use values from the parent SELECT
I know that this scenario can be easily accomplished in other ways, but the above is a simplified example to explain the challenge. My real scenario is a complex SELECT statement where I do not want to complicate it by adding more joins. Performance is no issue.
Using INNER JOIN:
SELECT C.CompanyName, Count(E.*) as EmployeeCount
FROM Company C
INNER JOIN Employees E on E.CompanyId = C.Id
Using NESTED JOIN:
SELECT C.CompanyName, Count(E.1) as EmployeeCount
FROM Company C, Employess E
WHERE E.CompanyId = C.Id
If you want to use the same syntax, at least put this:
SELECT C.CompanyName,
(SELECT Count(1) FROM Employees WHERE CompanyId = C.Id) as EmployeeCount
FROM Company C
If you need all the data to be shown, even the ones the companies without any Employees, you can use a LEFT OUTER JOIN:
SELECT C.CompanyName, Count(E.*) as EmployeeCount
FROM Company C
LEFT OUTER JOIN Employees E on E.CompanyId = C.Id
Try using a derived table, which statifies both your conditions.
An independent SELECT statement.
a. Using a Derived Table allows you to keep your independent Select Statement
Can use values from the parent SELECT.
a. As an Inner join you can still use values from the parent select.
SELECT
C.CompanyName,
EC.EmployeeCount
FROM Company C
INNER JOIN (SELECT
Count(*) AS EmployeeCount
FROM Employees ) EC
ON WHERE EC.CompanyId = C.Id
If your inner select is complicated, then why not make a view of it:
CREATE VIEW EmpSelect AS
SELECT CompanyId, whatever FROM Employees;
Then
SELECT
C.CompanyName, Count(*) AS EmpCount
FROM
Company C
LEFT JOIN EmpSelect E
ON C.Id = E.CompanyId
GROUP BY
C.CompanyName;