Using SQL to calculate average number of customers - mysql

Please see above for the data structure. I am trying to write an SQL query to get the average number of customers for each session
My attempt:
select avg(A.NumberCustomer)
from(
select SessionName, count(distinct customers.Idcustomer) as NumberCustomer,
from customers, enrollments, sessions
where customers.Idcustomer=enrollments.Idcustomer and enrollments.Idsession=sessions.Idsession
group by sessions.SessionName
) A
But I seem to get an error on the from customers, enrollments, sessions line
Not sure about this, any help appreciated.
Thanks

You have and extra comma that you should to delete:
select avg(A.NumberCustomer)
from(
select SessionName,
count(distinct customers.Idcustomer) as NumberCustomer, #<--- here
from customers, enrollments, sessions
where customers.Idcustomer=enrollments.Idcustomer
and enrollments.Idsession=sessions.Idsession
group by sessions.SessionName
) A
By the way, I suggest to you to move to SQL'99 join syntax for readability reasons:
SELECT
avg(A.NumberCustomer)
FROM (
select
SessionName,
count(distinct customers.Idcustomer) as NumberCustomer
from customers
inner join enrollments
on customers.Idcustomer=enrollments.Idcustomer
inner join sessions
on enrollments.Idsession=sessions.Idsession
group by sessions.SessionName
) A
Also, nice diagram on question and remember to include your error message next time.

For the average number of customers in each session, you should be able to use just the enrollments table. The average would be the number of enrollments divided by the number of sessions:
select count(*) / count(distinct idSession)
from enrollments e;
This makes the following assumptions:
All sessions have at least one customer (your original query had this assumption as well).
No customer signs up multiple times for the same session.

Related

Error Code 1111. Invalid use of group function in MySQL

The following image is the ER diagram of the database:
My task is to create a report that includes the following:
the storeID
the store name
the number of unique players that have purchased a badge from the store
the number of unique players that have not purchased a badge from the store
the total money spent at the store
the most expensive badge a player has purchased at the store
the cheapest badge a player has purchased at the store
the average price of the items that have been purchased at the store.
But when I am trying to execute the following SQL command, I am getting an error saying: Error Code 1111. Invalid use of group function.
use treasurehunters;
select rpt_category.storeId,
rpt_category.storeName,
total_purchased_user,
non_purchased_player,
total_spent,
expensive_badge,
cheapest_badge
avg_spent
from
(select badgename as expensive_badge
from badge
inner join purchase
where cost = max(cost)) rpt_data_2
inner join
(select badgename as cheapest_badge
from badge
inner join purchase
where cost = min(cost)) rpt_data_3
inner join
(select distinct count(username) as total_purchased_user,
storeid,
storename,
sum(cost) as total_spent,
average(cost) as avg_spent
from player
inner join purchase
inner join store
inner join badge
on store.storeID = purchase.storeID and
purchase.username= player.username and
purchase.badgeID = badge.badgeId) rpt_category
inner join
(select count (username) as non_purchased_player,
storeid
from player
inner join purchase
on purchase.storeid != store.storeid and
player.userername= purchase.uername ) rpt_data_1;
Now, what can I do to get rid of that error.
The cause of your error is likely that you're implying a store-level grouping without explicitly grouping on that column with a GROUP BY clause. Therefore, you're attempting to extract aggregate results that are impossible at the table-level.
You can probably resolve this by adding GROUP BY store.storeID in each of your subqueries. However, there's a lot more wrong with this query that makes it unfavorable to attempt to diagnose and resolve it.
This is all doable in a single query / grouping. Here's what your query should look like:
SELECT
store.storeID,
MAX(store.storeName) AS storeName,
COUNT(DISTINCT purchase.username) AS total_purchased_user,
MAX(player_count.players) - COUNT(DISTINCT purchase.username) AS non_purchased_user,
SUM(purchase.cost) AS total_spent,
AVG(purchase.cost) AS avg_spent,
SUBSTRING(MIN(CONCAT(LPAD(purchase.cost, 11, '0'), badge.badgeName)), 12) AS cheapest_badge,
SUBSTRING(MAX(CONCAT(LPAD(purchase.cost, 11, '0'), badge.badgeName)), 12) AS expensive_badge
FROM store
LEFT JOIN purchase ON store.storeID = purchase.storeID
LEFT JOIN badge ON purchase.badgeID = badge.badgeId
CROSS JOIN (SELECT COUNT(*) AS players FROM player) AS player_count
GROUP BY store.storeID;
What's happening here (working bottom-up):
GROUP BY store to ensure the results are aggregated by that, and all other metrics are calculated
FROM store / LEFT JOIN all other tables ensures we get metrics from every store, whether or not there are purchases for it
CROSS JOIN (SELECT COUNT(*) FROM players) this is a hack to give us a running total of all players that we can reference against store player-purchase counts to get the "didn't purchase" count simply and quickly, without any additional joins
COUNT(DISTINCT purchase.username) ensures that user counts are referenced from purchases. This also means we don't have to join on the players table in this main portion of the query to get purchase counts.
SUM / AVERAGE work like you had them
SUBSTRING(MIN(CONCAT... these calculations are using Scalar-Aggregate Reduction, a technique I invented to prevent the need for self-joining a query to get associated min/max values. There's more on this technique here: SQL Query to get column values that correspond with MAX value of another column?
Cheers!

Joining three tables and finding a sum of payments in MySQL

I am struggling with this problem in MySQL. The question asks...
Find the names of the individuals and businesses that have made no more than three payments.
Individuals is a table, businesses is a table, and payments is a table. The problem I am having is Payments only contains columns dateFiled and amountPaid. I tried creating a count operation, but it shows blank results.
Here is my code:
SELECT Individuals.name, Businesses.name, Payments.taxpayerID, COUNT(*) AS 'Payments'
FROM Payments
JOIN Individuals ON Payments.taxpayerID=Individuals.taxpayerID
JOIN Businesses ON Payments.taxpayerID=Businesses.taxpayerID
GROUP BY Businesses.name, Individuals.name, Payments.taxpayerID
HAVING COUNT(*) <= 3;
If anyone can help me solve this it would be greatly appreciated.
I guess what you are looking for is not a join of three tables but a union of two selects:
SELECT Individuals.name, Payments.taxpayerID, COUNT(*) AS 'Payments'
FROM Payments
JOIN Individuals ON Payments.taxpayerID=Individuals.taxpayerID
GROUP BY Individuals.name, Payments.taxpayerID
HAVING COUNT(*) <= 3
UNION
SELECT Businesses.name, Payments.taxpayerID, COUNT(*) AS 'Payments'
FROM Payments
JOIN Businesses ON Payments.taxpayerID=Businesses.taxpayerID
GROUP BY Businesses.name, Payments.taxpayerID
HAVING COUNT(*) <= 3;
Your version is giving zero results, because a tax ID is either associated with a business or an individual. Therefore you need to query both independently and combine the results with union.
That said, yes you could work with joins and only a single select but then you'd need outer joins and the query would be less readable IMHO.

MySQL COUNT Sub-query with SELECT *

I have a table of inventory items (holds description, details of item etc.), a table of stock (physical items that we have - items of inventory), and a suppliers table (who supply the stock, but may differ from time to time).
Suppliers -- Stock -- Inventory
Inventory has many stock. Suppliers have many stock. Stock has one supplier, and one inventory
I'm trying to run a query to get all data from inventory, and count how many suppliers it has through a sub query. However, I need to use SELECT *
What I have at the moment:
SELECT
( SELECT COUNT(DISTINCT SupplierID)
FROM Stock
WHERE Stock.InventoryID = Inventory.ID
) AS Suppliers
, *
FROM `Inventory`;
I've tried variations on this, swapping the field order (seen this elsewhere on this site), changing the sub-query etc.
However, it tells me there's an error near '* FROM'. Can anyone suggest a way to do this query please?
Use table aliases:
SELECT (SELECT COUNT(DISTINCT s.SupplierID)
FROM Stock s
WHERE s.InventoryID = i.ID
) AS Suppliers, i.*
FROM `Inventory` i;
The need for a qualification on * is described in the documentation:
Use of an unqualified * with other items in the select list may
produce a parse error. To avoid this problem, use a qualified
tbl_name.* reference
SELECT AVG(score), t1.* FROM t1 ...

MySQL: WHERE COUNT(*) = 0

I am trying to get all the customer_id's where no rows from have been found, for example:
SELECT customer_id FROM transaction WHERE count(*) = '0'
I have tried this aswell:
SELECT customer_id, count(*) as total_rows FROM transaction WHERE total_rows='0'
But I get the error that total_rows is not a column.
The easiest way to do this is to think about it in a bit of a different way: "how do I get a list of all customers who have no transaction history?"
Simple! You get a list of all of the customers, join it against their transactions and filter out any customers who have a non-empty list of transactions. Or, in SQL:
SELECT
customer.customer_id
FROM customer
LEFT JOIN transaction
ON transaction.customer_id = customer.customer_id
WHERE
transaction.transaction_id IS NULL
Note that you cannot simply use the transaction table like you're attempting. It is not a complete list of customer_id but rather it contains only IDs of customers who have an order.
Instead of operating on transaction and finding customers with no transactions (which you literally cannot do), you must find all customers and then filter by those who have no transactions. Similar concept, just opposite order.

Wrong use of inner join function / group function?

I have the following problem with my query:
I have two tables:
Customer
Subscriber
linked together by customer.id=subscriber.customer_id
in the subscriber table, I have records with id_customer=0 (these are email records, that do not have a full customer account)
Now i want to show how many customers I have per day, and how many subscribers with id_customer, and how many subscribers WITH id_customer=0 (emailonlies i call them)
Somehow, i cannot manage to get those emailonlies.
Perhaps it has something to do with not using the right join type.
When i use left join, i get the right amount of customers, but not the right amount of emailonlies. When I use inner join i get the wrong amount of customers. Am i using the group function correctly? i think it has something to do with that.
THIS IS MY QUERY:
` SELECT DATE(c.date_register),
COUNT(DISTINCT c.id) AS newcustomers,
COUNT(DISTINCT s.customer_id) AS newsubscribedcustomers,
COUNT(DISTINCT s.subscriber_id AND s.customer_id=0) AS emailonlies
FROM customer c
LEFT JOIN subscriber s ON s.customer_id=c.id
GROUP BY DATE(c.date_register)
ORDER BY DATE(c.date_register) DESC
LIMIT 10
;`
I'm not entirely sure, but I think in DISTINCT s.subscriber_id AND s.customer_id=0, it runs the AND before the DISTINCT, so the DISTINCT only ever sees true and false.
Why don't you just take
COUNT(DISTINCT s.subscriber_id) - (COUNT(DISTINCT s.customer_id) - 1)?
(The -1 is there because DISTINCT s.customer_id will count 0.)
Got it, only risk is that i get no email onlies if there are no customers on this day, becuase of the left join. But this one works:
SELECT customers.regdatum,customers.customersqty,subscribers.emailonlies
FROM (
(SELECT DATE(c.date_register) AS regdatum,COUNT(DISTINCT c.id) AS customersqty
FROM customer c
GROUP BY DATE(c.date_register)
) AS customers
LEFT JOIN
(SELECT DATE(s.added) AS voegdatum,COUNT(DISTINCT s.subscriber_id) AS emailonlies
FROM subscriber s
WHERE s.customer_id=0
GROUP BY DATE(s.added)
) AS subscribers
ON customers.regdatum=subscribers.voegdatum
)
ORDER BY customers.regdatum DESC
;