SQL Query, What am I doing wrong? - mysql

Hi there fellas I'm trying to do this query but I am having trouble with it. Could anyone give me a hand I'll list below what I've done.
How busy each optometrist has been. Your SQL statement should return the full names of all optometrists, and the total number of appointments they have conducted. You must use the word ‘Optometrist’ not the positionID to select optometrists in your statement. Note that even optometrists with zero appointments should be displayed in the results.
What I've done..
SELECT firstName, lastName, optometristID, COUNT(optometristID)
FROM employee
LEFT JOIN appointment ON employee.employeeID=appointment.optometristID
GROUP BY (optometristID)
The full name, email and primary phone number and total number of invoices for all customers in ascending order of last name. Note that even customers with zero invoices should be displayed in the results.
What I've wrote..
SELECT c.firstName, c.lastName, c.primaryPhone,
(SELECT count(*) from invoice where customerID = c.customerID) as numInvoices
FROM customer c, invoice i
WHERE c.customerID = c.customerID
ORDER BY lastname ASC
Thank you!

The first one is ok for me
The second should be either
SELECT c.firstName, c.lastName, c.primaryPhone,
(SELECT count(*) from invoice where customerID = c.customerID) as numInvoices
FROM customer c
ORDER BY lastname ASC
OR
SELECT c.firstName, c.lastName, c.primaryPhone,
count(i.customerID ) as numInvoices
FROM customer c left join invoice i
on i.customerID = c.customerID
group by c.customerID
ORDER BY lastname ASC
The last one should be faster

In second query you have to write
WHERE c.customerID = i.customerID
Instead of
WHERE c.customerID = c.customerID

Related

subquery shows more that one row group by

I am trying to get the data for the best 5 customers in a railway reservation system. To get that, I tried getting the max value by summing up their fare every time they make a reservation. Here is the code.
SELECT c. firstName, c.lastName,MAX(r.totalFare) as Fare
FROM customer c, Reservation r, books b
WHERE r.resID = b.resID
AND c.username = b.username
AND r.totalfare < (SELECT sum(r1.totalfare) Revenue
from Reservation r1, for_res f1, customer c1,books b1
where r1.resID = f1.resID
and c1.username = b1.username
and r1.resID = b1.resID
group by c1.username
)
GROUP BY c.firstName, c.lastName, r.totalfare
ORDER BY r.totalfare desc
LIMIT 5;
this throws the error:[21000][1242] Subquery returns more than 1 row
If I remove the group by from the subquery the result is:(its a tabular form)
Jade,Smith,1450
Jade,Smith,725
Jade,Smith,25.5
Monica,Geller,20.1
Rach,Jones,10.53
But that's not what I want, as you can see, I want to add the name 'Jade' with the total fare.
I just don't see the point for the subquery. It seems like you can get the result you want with a sum()
select c.firstname, c.lastname, sum(totalfare) as totalfare
from customer c
inner join books b on b.username = c.username
inner join reservation r on r.resid = b.resid
group by c.username
order by totalfare desc
limit 5
This sums all reservations of each client, and use that information to sort the resulstet. This guarantees one row per customer.
The query assumes that username is the primary key of table customer. If that's not the case, you need to add columns firstname and lastname to the group by clause.
Note that this uses standard joins (with the inner join ... on keywords) rather than old-school, implicit joins (with commas in the from clause: these are legacy syntax, that should not be used in new code.

Ordering rows in a parent table by SUM of column in a child table

I have 3 tables in my database
companies{
id,
name,
address
}
stores{
id,
name,
address,
company_id
}
invoices{
id,
total,
date_time,
store_id
}
As you can see, each store is connected to a company via foreign key, also each invoice is connected to a store.
My question is, how can i write a SQL query which will return all stores by a company and order them by their turnover?
If i use the query:
SELECT s.*,
sum(i.total) as turnover FROM store s
JOIN invoices i
ON i.store_id = s.id
WHERE YEAR(i.date_time) = 2019;
I can see the turnover for one store for a year 2019 for example, but i'm struggling to find a way to get a list of store ordered by their turnover for a certain period.
You're going to need to join all 3 tables:
SELECT *
FROM
companies c
INNER JOIN stores s on s.company_id = c.id
INNER JOIN invoices i ON i.store_id = s.id
That's your entire raw data in detailed list. Then you say you want it for a certain company only:
SELECT *
FROM
companies c
INNER JOIN stores s on s.company_id = c.id
INNER JOIN invoices i ON i.store_id = s.id
WHERE c.name = 'Acme Rubber Co'
Then you only want the stores and the invoices amounts:
SELECT s.name, i.total
FROM
companies c
INNER JOIN stores s on s.company_id = c.id
INNER JOIN invoices i ON i.store_id = s.id
WHERE c.name = 'Acme Rubber Co'
Then you want a row set where each line is a single store and the sum of all invoices for that store:
SELECT s.name, SUM(i.total)
FROM
companies c
INNER JOIN stores s on s.company_id = c.id
INNER JOIN invoices i ON i.store_id = s.id
WHERE c.name = 'Acme Rubber Co'
GROUP BY s.name
Lastly you want them in descending order, highest total first:
SELECT s.name as storename, SUM(i.total) as turnover
FROM
companies c
INNER JOIN stores s on s.company_id = c.id
INNER JOIN invoices i ON i.store_id = s.id
WHERE c.name = 'Acme Rubber Co'
GROUP BY s.name
ORDER BY turnover DESC
The order of evaluation in sql is FROM(with joins), WHERE, GROUP BY, SELECT, ORDER BY which is why I use different names in eg the order by than I do in the group by. Conceptually your db only sees the names of things as output by the immediately previous operation. Mysql isn't actually too picky but some db are - you couldn't say GROUP BY storename in sql server because the SELECT that creates the storename alias hasn't been run at the time the group by is done
Note: I wasn't really sure on what you were looking for in a WHERE - you started by saying "all stores turnover for a certain company" and finished saying you were "struggling to get turnover for a period"
If you want a period, use eg WHERE somedatecolumn BETWEEN '2000-01-01' AND '2000-12-31' (Between is inclusive) or WHERE somedatecolumn >= '2000-01-01' AND somedatecolumn < '2001-01-01' (A good pattern to use if the date includes a time too). It is almost never wise to call a function on a column you're searching with, ie do not do WHERE YEAR(somedatecolumn) = 2000 because it disables indexing on the column and makes the search very slow

SQL 3 - table query

was wondering if you could help me out with a SQL problem.
Example of the tables, please ingore the lack of relationships its just an example.
http://postimg.org/image/5imvjgl0t/
Right so basically I want to get all the CustomerID's associated with a certain countrycode I can get that by doing a simple query on the customer table however I then need to find out how many forms the customer has submitted in the order table.
I then need to get the title of the warehouse (one warehouse per countrycode) and the number of customers associated with that warehouse who have submitted a form.
So in summary I need to get a list of customerID's from the Customer table and count how many time they show up in the Order table. sort them into the warehouse echo the warehouse title with the number of orders.
What's the easiest way to go about this?
P.S. please ignore any data type / relationship issues with the image above, it's just an example.
SELECT w.title, c.CustomerID, COUNT(o.form_id) as cnt
FROM warehouse w
JOIN customer c USING (countrycode)
JOIN `Order` o USING (CustomerID)
GROUP BY w.siteid, c.CustomerID
ORDER BY w.title ASC, c.CustomerID
edit
If you want to include customers without any orders, you need a LEFT JOIN on the customers table:
SELECT w.title, c.CustomerID, COUNT(o.form_id) as cnt
FROM warehouse w
JOIN customer c USING (countrycode)
LEFT JOIN `Order` o USING (CustomerID)
GROUP BY w.countrycode, c.CustomerID
ORDER BY w.title ASC, c.CustomerID
To filter for only one customer:
SELECT w.title, c.CustomerID, COUNT(o.form_id) as cnt
FROM warehouse w
JOIN customer c USING (countrycode)
LEFT JOIN `Order` o USING (CustomerID)
WHERE c.CustomerId = 2
GROUP BY w.countrycode, c.CustomerID
ORDER BY w.title ASC, c.CustomerID

Error with SQL code

Trying to execute this code with COALESCE to group data together. When executing I see this error
Msg 207, Level 16, State 1, Line 11
Invalid column name 'OrderID'.
Msg 209, Level 16, State 1, Line 1
Ambiguous column name 'CustomerID'.
SELECT CustomerID,
FirstName,
LastName,
COALESCE(OrderIDCnt,0),
COALESCE(SKUCnt,0),
COALESCE(OrderTotal,0)
FROM Customer as c
left join (SELECT o.CustomerID,
SUM(OrderTotal) AS OrderTotal,
COUNT(OrderedProductSKU) AS SKUCnt,
COUNT(OrderID) AS OrderIDCnt
FROM Orders as o
inner join Orders_ShoppingCart as osc
on osc.OrderNumber=o.OrderNumber
and osc.CustomerID=o.CustomerID
GROUP BY o.CustomerID
)ord
ON ord.CustomerID = c.CustomerID
try to use c.CustomerId or ord.CustomerId
The error means that SQL parser cannot uniquely resolve the unqialified name CustomerID. There are two candidates there:
The CustomerID field of the Customer table, and
The CustomerID field of the ord sub-query.
Although the two must match, because your join condition requires that ord.CustomerID = c.CustomerID, the query parser cannot arbitrarily pick one for you. Therefore, you need to either disambiguate this manually by specifying c or ord in front of CustomerID, or pick a different name for CustomerID in the subquery.
The first approach:
SELECT c.CustomerID, -- Add c. in front of CustomerID
FirstName,
LastName,
COALESCE(OrderIDCnt,0),
COALESCE(SKUCnt,0),
COALESCE(OrderTotal,0)
FROM Customer as c
left join (SELECT o.CustomerID,
SUM(OrderTotal) AS OrderTotal,
COUNT(OrderedProductSKU) AS SKUCnt,
COUNT(o.OrderID) AS OrderIDCnt -- Disambiguated OrderID
FROM Orders as o
inner join Orders_ShoppingCart as osc
on osc.OrderNumber=o.OrderNumber
and osc.CustomerID=o.CustomerID
GROUP BY o.CustomerID
)ord
ON ord.CustomerID = c.CustomerID
The second approach:
SELECT CustomerID,
FirstName,
LastName,
COALESCE(OrderIDCnt,0),
COALESCE(SKUCnt,0),
COALESCE(OrderTotal,0)
FROM Customer as c
left join (SELECT o.CustomerID as OrderCustomerID, -- add an alias
SUM(OrderTotal) AS OrderTotal,
COUNT(OrderedProductSKU) AS SKUCnt,
COUNT(o.OrderID) AS OrderIDCnt -- Disambiguated OrderID
FROM Orders as o
inner join Orders_ShoppingCart as osc
on osc.OrderNumber=o.OrderNumber
and osc.CustomerID=o.CustomerID
GROUP BY o.CustomerID
)ord
ON ord.OrderCustomerID = c.CustomerID -- Rename the field
Edit : Disambiguated OrderID.
You have field named customerId in tables Orders and Customer and you don't specify which you want to include in select clause.
In this case you always should type the field in format alias.field

Error 1111 (HY000) - Invalid use of group function

I'm getting a problem when trying to run this query:
Select
c.cname as custName,
count(distinct o.orderID) as No_of_orders,
avg(count(distinct o.orderID)) as avg_order_amt
From Customer c
Inner Join Order_ o
On o.customerID = c.customerID
Group by cname;
This is an error message: #1111 (HY000) - Invalid use of group function
I just want to select each customer, find how many orders each customer has, and average the total number of orders for each customer. I think it might have a problem with too many aggregates in query.
The issue is that you need to have two separate groupings if you want to calculate the average over a count, so this expression isn't valid:
avg(count(distinct o.orderID))
Now it's hard to understand what exactly you mean, but it sounds as if you just want to use avg(o.amount) instead.
[edit] I see your addition now, so while the error is still the same, the solution will be slightly more complex. The last value you need, the avarage number of orders per customer, is not a value to calculate per customer. You'd need analytical functions to that, but that might be quite tricky in MySQL. I'd recommend to write a separate query for that, otherwise you would have very complex query which would return the same number for each row anyway.
select c.cname, o.customerID, count(*), avg(order_total)
from order o join customer using(customerID)
group by 1,2
This will calculate the number of orders and average order total (substitute the real column name for order_total) for each customer.
how many orders each customer has,
average the total number of orders.
SELECT
c1.cname AS custName,
c1.No_of_orders,
c2.avg_order_amt
FROM (
SELECT
c.id,
c.cname,
COUNT(DISTINCT o.orderID) AS No_of_orders
FROM
Customer c
JOIN Order_ o ON o.customerID = c.customerID
GROUP BY c.id, c.cname
) c1
CROSS JOIN (SELECT AVG(No_of_orders) AS avg_order_amt FROM (
SELECT
c.id,
COUNT(DISTINCT o.orderID) AS No_of_orders
FROM
Customer c
JOIN Order_ o ON o.customerID = c.customerID
GROUP BY c.id
)) c2