SQL query on 3 tables with one to many relationships - mysql

I need help on figuring out the SQL query for my e-commerce site
there are Users(customers / customer-service-reps) table
there are Orders table
there are Line-Items(columns are manufacturer, quantity, ect) table
Users have many Orders, and Orders have many Line-Items.
I am trying to find list of users who has made 1 or more order which includes items from ('X-Parts' <- name of manufacturer)
Any help would be greatly appreciated

Try This
SELECT U.UserID, COUNT(O.OrderID) OrderCount
FROM Users U INNER JOIN Orders O ON U.UserID = O.UserID
INNER JOIN Line-Items L ON O.OrderID = L.OrderID
Where L.manufacturer = 'X-Parts'
Group BY U.UserID
Having count(O.orderID) >= 1
Sample Demo:- http://sqlfiddle.com/#!3/f1712/2

Its one or more orders.
SELECT U.UserID, COUNT(O.OrderID) as OrderCount
FROM Users U
INNER JOIN Orders O ON U.UserID = O.UserID
INNER JOIN Line-Items L ON O.OrderID = L.OrderID
Where L.manufacturer = 'X-Parts'
Group BY U.UserID
Having count(O.orderID) >= 1

Related

How to make SQL code for getting top 100 distributors faster?

I have this database based on this file sql file. The database contains random data, not real data.
The goal is to get the top 100 distributors with the most sales. Sales are the total amount of orders that have been purchased by the customers and distributors they have referred and if 2 or more Distributors have achieved the same amount of sales, they would have the same rank.
I already have working sql code but it is slow. It takes about 4 minutes to complete.
Here is my MySQL code;
WITH items_total AS
(
SELECT
(oi.quantity * p.price) AS total, oi.order_id
FROM
order_items oi
INNER JOIN
products p on p.id = oi.product_id
),
orders_total AS
(
SELECT
o.id, o.purchaser_id, SUM(it.total) AS total
FROM
orders o
INNER JOIN
items_total it ON it.order_id = o.id
GROUP BY
o.id
),
distributors AS
(
SELECT
u.id, u.first_name, u.last_name, c.name
FROM
users u
INNER JOIN
user_category uc ON uc.user_id = u.id
INNER JOIN
categories c ON c.id = uc.category_id
WHERE
c.id = 1
GROUP BY u.id
)
SELECT
d.first_name, d.last_name,
SUM(ot.total) AS total_sales, d.id,
DENSE_RANK() OVER (ORDER BY total_sales DESC) AS 'rank'
FROM
distributors d
INNER JOIN
users uu ON uu.referred_by = d.id
INNER JOIN
orders_total ot ON ot.purchaser_id = uu.id
GROUP BY
d.id
ORDER BY
total_sales DESC
LIMIT 100
How can I make it run faster?
I see a PRIMARY KEY on one table; each table needs a PK. I see no other INDEXes.
The performance of queries depends on Indexes.

Get all names of users who have placed car orders totalling more than 40000$

I have the following tables:
Cars (CarID, Colour, Price)
CarOrders(CarID, OrderID, AmountOfCars)
Orders(OrderID, UserID)
Users (UserID, Name)
I'd like to query:
all names of users who have placed car orders totalling more than 40000 (also considering amount, if a user buys the same car multiple times), alongside the number of these orders. The desired results format is: (Users.name, Number)
I have tried the following:
SELECT *
FROM Users
JOIN (
SELECT a.UserID, SUM(a.AmountOfCars*b.Price) total
FROM Orders a
JOIN Cars b, ON b.CarID == a.CarID,
GROUP BY a.UserID
) c ON c.UserID == c.UserID
WHERE c.total > 20000
SELECT
U.Name,
SUM(C.Price * CO.AmountOfCars) AS 'Price'
FROM Users U
JOIN Orders O ON U.UserID = O.UserID
JOIN CarOrders CO ON CO.OrderID = O.OrderID
JOIN Cars C ON C.CarID = CO.CarID
GROUP BY U.Name
HAVING SUM(C.Price * CO.AmountOfCars) > 40000
You're referencing columns that don't exist in the tables you specify. The Order table doesn't have CarID or amountOfCars. Those are in CarOrders, you have to join with that to relate orders with cars.
ON c.UserID == c.UserID makes no sense at all, that will always be true. You meant ON Users.UserID = c.UserID.
I recommend using aliases that are more mnemonic than a, b, c, etc. Abbreviations for the table names are better.
SELECT u.name
FROM Users AS u
JOIN (
SELECT o.UserID
FROM Orders AS o
JOIN CarOrders AS co ON co.OrderID = o.OrderID
JOIN Cars AS c on co.CarID = c.CarID
GROUP BY o.UserID
HAVING SUM(co.amountOfCars * c.Price) > 40000
) AS o ON o.UserID = u.UserID

inner-join mysql x3

I need to display cust_id, the customer forename and surname, the product name<-(from products table) and date of sale<--(from sales table), also I need to display in order of the most recent dates first.
This is what I have got so far:
SELECT
customer.cust_id,
customer.forename,
customer.surname,
products.prod_name,
sales.Date_of_sale
FROM customers c
INNER JOIN sales s ON c.cust_id = s.cust_id
INNER JOIN products p ON s.product_id = p.product_id
ORDER BY s.Date_of_sale DESC
Any help would be appreciated.
This
SELECT
c.cust_id,
c.forename,
c.surname,
p.prod_name,
s.Date_of_sale
FROM customers c
INNER JOIN sales s ON c.cust_id = s.cust_id
INNER JOIN products p ON s.product_id = p.product_id
ORDER BY s.Date_of_sale DESC
will work

query returns more than one row

I'm trying to get the total rental amount for each client from Sakila example database
so I tried with the following query:
select customer.customer_id, customer.first_name,
(select sum(payment.amount) from customer
inner join rental on customer.customer_id=rental.customer_id
inner join payment on rental.rental_id=payment.rental_id group by payment.amount)
from customer
inner join rental on customer.customer_id=rental.customer_id
inner join payment on rental.rental_id=payment.rental_id
group by customer.customer_id;
and I get this "Subquery returns more than one row". Do you know what could be wrong? Thank you
This is your query, with some reformatting and the use of table aliases:
select c.customer_id, c.first_name,
(select sum(p2.amount)
from customer ce inner join
rental r2
on c2.customer_id = r2.customer_id inner join
payment p2
on r2.rental_id = p2.rental_id
group by p2.amount
-------^
)
from customer c inner join
rental r
on c.customer_id = r.customer_id inner join
payment p
on r.rental_id = p.rental_id
group by c.customer_id;
I've highlighted the specific cause of your problem. But the fix is to radically simplify the query:
select c.customer_id, c.first_name, sum(p.amount)
from customer c left join
rental r
on c.customer_id = r.customer_id left join
payment p
on r.rental_id = p.rental_id
group by c.customer_id;
Is that the result you're looking for?
SELECT C.customer_id
,C.first_name
,SUM(P.amount) AS [total_amount]
FROM customer C
INNER JOIN rental R ON R.customer_id = C.customer_id
INNER JOIN payment P ON P.rental_id = R.rental_id
GROUP BY C.customer_id, C.first_name
-- Condition to get only the largest amount
-- without using an ORDER BY clause
HAVING SUM(P.amount) = (SELECT MAX(SUM(P2.amount))
FROM rental R2
INNER JOIN payment P2 ON P2.rental_id = R2.rental_id
GROUP BY R2.customer_id)
Hope this will help you.

Transform sub-queries to joins

I have three tables: userProfile, loginTimes, orders.
I am trying get each user's profile row, his last login time, and his last order row.
Here's my query:
Select u.*, t.loginTime, orders.* From userProfiles u
Inner Join
(Select userId, MAX(time) loginTime From loginTimes Group By userID) t
On u.userId = t.userID
Inner Join
(Select userId, MAX(enterDate) orderDate From orders Group By userId) o
On u.userID = o.userID
Inner Join
orders On orders.userId = u.userId And orders.enterDate = o.orderDate
Is there any way to rewrite without so many sub queries?
OP I think this is the query you are going for, this still requires 2 subqueries, but I don't believe your original query functioned as intended.
You could remove the loginTimes subquery, and use MAX(loginTime) in the outer SELECT list, but then you'd need to GROUP BY every field in the order table, which is arguably just as unclean.
The following query retrieves the UserId, latest LoginTime and the entire order record for the user's most recent order:
SELECT u.userId,
u.userName,
l.loginTime,
o.*
FROM userProfiles u
INNER JOIN ( SELECT userId,
loginTime = MAX(time)
FROM loginTimes
GROUP BY userID) l ON u.userId = l.userId
INNER JOIN ( SELECT *,
rowNum = ROW_NUMBER() OVER (PARTITION BY userId
ORDER BY enterDate DESC)
FROM orders) o ON u.userId = o.userId AND o.rowNum = 1
Working on SQLFiddle
You can easily re-write the aggregation with the following.
-- Aggregation
(
SELECT
T.userId,
MAX(time) as loginTime,
MAX(enterDate) as orderDate
FROM
loginTimes as T INNER JOIN orders as O
ON
T.userId = O.userId
GROUP BY
T.userId
)
However, I do not understand why you are calculating MAX(enterDate) and not using it.
The two tables that are joined without aggregation is easy also. You should stay away from using *. It is just wasted overhead if all the fields are not being used.
SELECT
U.*, O.*
FROM
userProfiles as U
INNER JOIN
orders as O
ON O.userId = U.userId
Please explain what you are trying to return as values from the Query. What is the business logic?
I believe this will do:
SELECT u.userID
,u.otherColumn
,MAX(t.time) AS loginTime
,MAX(o.enterDate) AS orderDate
FROM userProfiles u
JOIN loginTimes t ON t.userID = u.userID
JOIN orders o ON o.userID = u.userID
GROUP BY u.userID, u.otherColumn
For every other column in userProfiles you add to the SELECT clause, you need to add it to the GROUP BY clause as well..
Update:
Just because it can be done.. I tried it without any subquery :)
SELECT u.userID
,MAX(t.time) AS loginTime
,o.*
FROM userProfiles u
JOIN loginTimes t ON t.userID = u.userID
JOIN orders o ON o.userID = u.userID
LEFT JOIN orders o1 ON o.userID = o1.userID AND o.enterDate < o1.enterDate
WHERE o1.orderID IS NULL
GROUP BY u.userID
,o.* --write out the fields here
You'll have to write down the fields of the orders table you want in the select clause in your GROUP BY clause also.