SQL 4 tables inner join pick up sum Nulls also? - sql

I have 4 tables.... Employees, Customers, Orders and Order_Info. I am trying to inner join the 4 tables to sum up the order amounts and calculate the employees commission based on 7%. I am very close to solving this but I have one slight problem: I am not getting all of the employees because less are showing up than exist in my Employees table. This is how I currently have my query written:
SELECT Employees.lName, Employees.fName,
SUM(quantOrdered * costEach) AS ttl_orders_value,
(SUM(quantOrdered * costEach) * .07) AS Commission
FROM Customers
INNER JOIN Employees ON Customers.empNumber = Employees.empNumber
INNER JOIN Orders ON Customers.custNumber = Orders.custNumber
INNER JOIN Order_Info ON Orders.ordNumber = Order_Info.ordNumber
GROUP BY Employees.lName, Employees.fName
ORDER BY Employees.lName, Employees.fName
I wish to get all employees even if their commission and total sales equal zero, which I believe to be calculated from NULLS.
Any help improving my query would be greatly appreciated!

try this:
SELECT Employees.lName, Employees.fName,
SUM(ISNULL(quantityOrdered,0) * ISNULL(priceEach,0)) AS ttl_orders_value,
(SUM(ISNULL(quantOrdered,0) * ISNULL(costEach,0)) * .05) AS Commission
FROM Employees
LEFT JOIN Customers ON Customers.empNumber = Employees.empNumber
LEFT JOIN Orders
INNER JOIN OrderDetails ON Orders.ordNumber = OrderDetails.ordNumber
ON Customers.custNumber = Orders.custNumber
WHERE Employees.workTitle = 'Developer'
GROUP BY Employees.lName, Employees.fName
ORDER BY Employees.lName, Employees.fName
Note, I have changed INNER JOIN to LEFT JOIN only for Orders table because as you say Employees records should be there, they only may not have linked orders.
You may also need to wrap NULL priceEach, costEach, quantityOrdered and quantOrdered values with ISNULL([field_name], 0) to get proper results for those employees not having any orders.

Related

(Simple?) SQL Query: Display all customer information for customers with 2+ orders

I'm doing practice exam material for a distance education course. I have the following three relations (simplified here):
salesperson(emp#, name, salary)
order(order#, cust#, emp#, total)
customer(cust#, name, city)
I'm stuck on a pair of SQL queries.
Display all customer info for customers with at least 1 order.
SELECT * FROM customer
INNER JOIN order ON order.cust# = customer.cust#
GROUP BY cust#;
Display all customer info for customers with at least 2 orders.
SELECT cust#, name, city, industry-type FROM customer
INNER JOIN order ON order.cust# = customer.cust#
GROUP BY cust#
HAVING COUNT(cust#) > 2;
I realize these are misguided attempts resulting from a poor understanding of SQL, but I've spent a ton of time on W3School's SQL Query example tool (https://www.w3schools.com/sql/trysql.asp?filename=trysql_select_where) without getting anywhere, and I finally need some "real" help.
You can try to use subquery to get count by cust# then do inner join to make it.
SELECT c.*
FROM (
SELECT cust# , COUNT(*) cnt
FROM order
GROUP BY cust#
) o INNER JOIN customer c ON c.cust# = o.cust#
WHERE o.cnt > 2
You can change table names according to your DB. Following queries you can directly run in W3Schools
Display all customer info for customers with at least 1 order.
SELECT * FROM customers as cust JOIN orders as o ON o.customerid =
cust.customerid GROUP BY o.customerid;
Display all customer info for customers with at least 2 orders.
SELECT * FROM customers as cust JOIN orders as O ON O.CustomerID = cust.CustomerID GROUP BY cust.CustomerID HAVING COUNT(cust.CustomerID) > 2;

SQL dividing a count from one table by a number from a different table

I am struggling with taking a Count() from one table and dividing it by a correlating number from a different table in Microsoft SQL Server.
Here is a fictional example of what I'm trying to do
Lets say I have a table of orders. One column in there is states.
I have a second table that has a column for states, and second column for each states population.
I'd like to find the order per population for each sate, but I have struggled to get my query right.
Here is what I have so far:
SELECT Orders.State, Count(*)/
(SELECT StatePopulations.Population FROM Orders INNER JOIN StatePopulations
on Orders.State = StatePopulations.State
WHERE Orders.state = StatePopulations.State )
FROM Orders INNER JOIN StatePopulations
ON Orders.state = StatePopulations.State
GROUP BY Orders.state
So far I'm contending with an error that says my sub query is returning multiple results for each state, but I'm newer to SQL and don't know how to overcome it.
If you really want a correlated sub-query, then this should do it...
(You don't need to join both table in either the inner or outer query, the correlation in the inner query's where clause does the 'join'.)
SELECT
Orders.state,
COUNT(*) / (SELECT population FROM StatePopulation WHERE state = Orders.state)
FROM
Orders
GROUP BY
Orders.state
Personally, I'd just join them and use MAX()...
SELECT
Orders.state,
COUNT(*) / MAX(StatePopulation.population)
FROM
Orders
INNER JOIN
StatePopulation
StatePopulation.state = Orders.state
GROUP BY
Orders.state
Or aggregate your orders before you join...
SELECT
Orders.state,
Orders.order_count / StatePopulation.population
FROM
(
SELECT
Orders.state,
COUNT(*) AS order_count
FROM
Orders
GROUP BY
Orders.state
)
Orders
INNER JOIN
StatePopulation
StatePopulation.state = Orders.state
(Please forgive typos and smelling pistakes, I'm doing this on a phone.)

SQL - Display Count of records even if 0

I am trying to create an SQL query in MS Access that will show how many appointments an employee will have in the current month, even if it is 0. It is very similar to this question, but i can't get it to work with a WHERE clause.
I have 3 tables:
tblEmployees
-employeeID PK
-FirstName ETC
tblEngineersAppts1
-ApptID PK
-EmployeeID*
tblEngineersAppts2
-DiaryID PK
-ApptDate
-ApptID*
I want to show all employees, a COUNT of all appointments (DiaryID) in tblEngineersAppts2 even if there are none where ApptDate is the current month.
This is my query, it only shows employees that have an appointment in the current month, it doesn't show those who have none.
SELECT tblEmployees.EmployeeID, Count(tblEngineersAppts2.DiaryID) AS CountOfDiaryID
FROM (tblEmployees
LEFT JOIN tblEngineersAppts1 ON tblEmployees.EmployeeID = tblEngineersAppts1.EmployeeID)
LEFT JOIN tblEngineersAppts2 ON tblEngineersAppts1.ApptID = tblEngineersAppts2.ApptID
WHERE (((Format$([ApptDate],'MM/YY'))='03/17'))
GROUP BY tblEmployees.EmployeeID;
Thanks
The problem is when you put the WHERE condition you make the LEFT JOIN an INNER JOIN
WHERE (((Format$([ApptDate],'MM/YY'))='03/17'))
So Include the ApptDate constraint in the ON condition.
SELECT tblEmployees.EmployeeID, Count(tblEngineersAppts2.DiaryID) AS CountOfDiaryID
FROM (tblEmployees
LEFT JOIN tblEngineersAppts1 ON tblEmployees.EmployeeID = tblEngineersAppts1.EmployeeID)
LEFT JOIN tblEngineersAppts2
ON ( tblEngineersAppts1.ApptID = tblEngineersAppts2.ApptID
AND Format$([ApptDate],'MM/YY')='03/17'
)
GROUP BY tblEmployees.EmployeeID;
I think you could do something like this:
SELECT
tblEmployees.EmployeeID,
(
SELECT Count(tblEngineersAppts2.DiaryID)
FROM tblEngineersAppts1
JOIN tblEngineersAppts2 ON tblEngineersAppts1.ApptID = tblEngineersAppts2.ApptID
WHERE tblEmployees.EmployeeID = tblEngineersAppts1.EmployeeID
AND (((Format$([ApptDate],'MM/YY'))='03/17'))
) AS CountOfDiaryID
FROM
tblEmployees

SQL server SELECT with join performance issue

Sorry about the saga here but am trying to explain everything.
We have 2 databases that I would like to join some tables in.
1 database holds sales data from various different stores/sites. This database is quite large (over 3mill rows currently) This table is ItemSales
The other holds application data from an in house web app. These tables are Departments and GroupItems
I would like to create a query that joins 2 tables from the app database with the sales database table. This is so we can group some items together for a date range and see the amount sold for example.
My first attempt was (DealId being the variable that it is grouped on in the App):
SELECT d.Id, d.ItemNo, d.UnitValue, d.NoGST, d.ItemStartDate, d.ItemEndDate,
(SELECT SUM(ItemQty) AS Expr1
FROM Sales.dbo.ItemSales AS s
WHERE (Store = d.SiteId) AND (ItemNo = d.ItemNo) AND (ItemSaleDate >= d.ItemStartDate) AND (ItemSaleDate <= d.ItemEndDate)) AS ItemsSold, Sales.dbo.ItemSales.ItemDesc, Departments.Description
FROM Departments INNER JOIN
Sales.dbo.ItemSales ON Departments.Id = Sales.dbo.ItemSales.ItemDept RIGHT OUTER JOIN
GroupItems AS d ON Sales.dbo.ItemSales.ItemNo = d.ItemNo
WHERE (d.DealId = 11)
GROUP BY d.Id, d.ItemNo, d.UnitValue, d.NoGST, d.ItemStartDate, d.ItemEndDate, ItemDesc, Departments.Description, d.SiteId
ORDER BY d.Id
This does exactly what I want which is:
-Give me all the details from the GroupItems table (UnitValue, ItemStartDate, ItemEndDate etc)
-Gives me the SUM() on the ItemQty column for the amount sold (plus the description etc)
-Returns NULL for something with no sales for the period
It is VERY slow though. To the point that if the GroupItems table has more than about 7 items in it, it times out.
Second attempt has been:
SELECT d.Id, d.ItemNo, d.UnitValue, d.NoGST, d.ItemStartDate, d.ItemEndDate, SUM(ItemQty) AS ItemsSold, Sales.dbo.ItemSales.ItemDesc, Departments.Description
FROM Departments INNER JOIN
Sales.dbo.ItemSales ON Departments.Id = Sales.dbo.ItemSales.ItemDept RIGHT OUTER JOIN
GroupItems AS d ON Sales.dbo.ItemSales.ItemNo = d.ItemNo
WHERE (Store = d.SiteId) AND (d.DealId = 11) AND (Sales.dbo.ItemSales.ItemSaleDate >= d.ItemStartDate) AND (Sales.dbo.ItemSales.ItemSaleDate <= d.ItemEndDate)
GROUP BY d.Id, d.ItemNo, d.UnitValue, d.NoGST, d.ItemStartDate, d.ItemEndDate, ItemDesc, Departments.Description
ORDER BY d.Id
This is very quick and does not time out but does not return the NULLs for no sales items in the ItemSales table. This is a problem as we need to see nothing or 0 for a no sales item otherwise people will think we forgot to check that item.
Can someone help me come up with a query please that returns everything from the GroupItems table, shows the SUM() of items sold and doesn't time out? I have also tried a SELECT x WHERE EXISTS (Subquery) but this also didn't return the NULLs for me but I may have had that one wrong.
If you want everything from GroupItems regardless of the sales, use it as the base of the query and then use left outer joins from there. Something along these lines:
SELECT GroupItems.Id, GroupItems.ItemNo, GroupItems.UnitValue, GroupItems.NoGST,
GroupItems.ItemStartDate, GroupItems.ItemEndDate,
Sales.ItemDesc,
SUM(ItemQty) AS SumOfSales,
Departments.Description
FROM GroupItems
LEFT OUTER JOIN #tempSales AS Sales ON
Sales.ItemNo = GroupItems.ItemNo
AND Sales.Store = GroupItems.SiteId
AND Sales.ItemSaleDate >= GroupItems.ItemStartDate
AND Sales.ItemSaleDate <= GroupItems.ItemEndDate
LEFT OUTER JOIN Departments ON Departments.Id = Sales.ItemDept
WHERE GroupItems.DealId = 11
GROUP BY GroupItems.Id, GroupItems.ItemNo, GroupItems.UnitValue, GroupItems.NoGST,
GroupItems.ItemStartDate, GroupItems.ItemEndDate,
Sales.ItemDesc,
SUM(ItemQty) AS SumOfSales,
Departments.Description
ORDER BY GroupItems.Id
Does changing the INNER JOIN to Sales.dbo.ItemSales into a LEFT OUTER JOIN to Sales.dbo.ItemSales and changing the RIGHT OUTER JOIN to GroupItems into an INNER JOIN to GroupItems fix your issue?

SQL statement to get all customers with no orders

I have a typical Persons table and an Orders table defined in such a way that I can do JOIN query as the following to return Orders for all Persons.
SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo
FROM Persons
INNER JOIN Orders
ON Persons.id=Orders.Person_id
The question is, how do I write a statement that would return all Persons with NO Orders?
I'm using mysql.
Thank all in advance.
You may want to use LEFT JOIN and IS NULL:
SELECT Persons.LastName, Persons.FirstName
FROM Persons
LEFT JOIN Orders ON Persons.id = Orders.Person_id
WHERE Orders.Person_id IS NULL;
The result of a left join always contains all records of the "left" table (Persons), even if the join-condition does not find any matching record in the "right" table (Orders). When there is no match, the columns of the "right" table will NULL in the result set.
This should work... theres more than one way to do it.
select * from persons where person.id not in (select person_id from orders)
Just for completeness, here is the not exists version:
select * from persons p
where not exists
(select null from orders o where o.person_id = p.id)
You can use left join:
SELECT DISTINCT o.CustomerID from Orders as o
left join Customers as c
on o.CustomerID=c.CustomerID
Question
Find customers who have never made an order.
Output the first name of the customer.
Data
Two tables: Customers and Orders
SELECT first_name
from customers
WHERE first_name not in
(select first_name
from customers
join orders on customers.id=orders.cust_id)